Skip to content

Commit

Permalink
Love for collectors (#1627)
Browse files Browse the repository at this point in the history
  • Loading branch information
cgay authored Nov 5, 2024
2 parents 8fa5f5a + afad952 commit d10a340
Show file tree
Hide file tree
Showing 10 changed files with 198 additions and 22 deletions.
110 changes: 102 additions & 8 deletions documentation/source/library-reference/collections/collectors.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,111 @@ The collectors Module
.. current-library:: collections
.. current-module:: collectors

.. macro:: collecting
:statement:

Collect values into a named or unnamed collector. A collector may be, for example, a
:drm:`<collection>`, a number into which values are accumulated, etc.

:macrocall:
.. parsed-literal::
collecting ([`name`] [as `type`])
[ `body` ]
end [ collecting ]
:parameter name: A Dylan variable-name *BNF*. If omitted, the collection is returned
from the :macro:`collecting` macro call. If supplied, the caller is responsible for
calling ``collected(name)`` to retrieve the collection before the call to
`collecting` terminates.
:parameter type: A Dylan type. The default value is :drm:`<list>`.
:parameter body: A Dylan body *BNF*.

:description:

Binds *name* (or a default variable name if *name* is not supplied) to a collector
that can efficiently collect new values into the collection when :macro:`collect`
or the related ``collect-*`` macros are called.

:example:

.. code-block:: dylan
collecting () collect(1); collect(2) end;
// => #(1, 2)
collecting () collect(1); collect-first(2) end;
// => #(2, 1)
collecting (as <integer>) collect(1); collect(2) end;
// => 3
collecting (a, b, c)
collect-into(a, 1);
collect-into(b, 2);
collect-into(c, 3);
values(collected(a), collected(b), collected(c)
end;
// => #(1), #(2), #(3)
.. macro:: collect

:description: Collect a value at the end of the unnamed collector: ``collect(100)``
May only be used when ``collecting () ... end`` was called with no arguments.

:seealso: :macro:`collecting`

.. macro:: collect-first

.. macro:: collect-first-into
:description: Collect a value at the beginning of the unnamed collector:
``collect-first(100)`` May only be used when ``collecting () ... end`` was called
with no arguments.

.. macro:: collect-into
:seealso: :macro:`collecting`

.. macro:: collect-last

:description: Collect a value at the end of the unnamed collector:
``collect-last(100)`` May only be used when ``collecting () ... end`` was called
with no arguments.

:seealso: :macro:`collecting`

.. macro:: collect-into

:description: Collect a value at the end of a named collector: ``collect-into(c,
100)`` May only be used when ``collecting (c) ... end`` was called with arguments.

:seealso: :macro:`collecting`

.. macro:: collect-first-into

:description: Collect a value at the beginning of a named collector:
``collect-first-into(c, 100)`` May only be used when ``collecting (c) ... end`` was
called with arguments.

:seealso: :macro:`collecting`

.. macro:: collect-last-into

:description: Collect a value at the end of a named collector: ``collect-last-into(c,
100)`` May only be used when ``collecting (c) ... end`` was called with arguments.

:seealso: :macro:`collecting`

.. macro:: collected

.. macro:: collecting
:description: Retrieve the value of the collection associated with a collector.

:example:

.. code-block:: dylan
collecting () ... map(f, collected()) ... end
collecting (a, b) ... map(f1, collected(a)); map(f2, collected(b)); ... end
:seealso: :macro:`collecting`

.. generic-function:: collector-protocol
:open:
Expand All @@ -28,9 +118,13 @@ The collectors Module

:parameter class: An instance of :drm:`<object>`.
:value new-collector: An instance of :drm:`<object>`.
:value add-first: An instance of :drm:`<function>`.
:value add-last: An instance of :drm:`<function>`.
:value add-sequence-first: An instance of :drm:`<function>`.
:value add-sequence-last: An instance of :drm:`<function>`.
:value collection: An instance of :drm:`<function>`.
:value add-first: A :drm:`<function>` that accepts the collection and a value and adds
the value to the beginning of the collection.
:value add-last: A :drm:`<function>` that accepts the collection and a value and adds
the value to the end of the collection.
:value add-sequence-first: An instance of :drm:`<function>`. **Not yet implemented.**
:value add-sequence-last: An instance of :drm:`<function>`. **Not yet implemented.**
:value collection: A :drm:`<function>` that receives the collector and returns the
collection.

:seealso: :macro:`collecting`
8 changes: 7 additions & 1 deletion sources/collections/collectors-macros.dylan
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ License: See License.txt in this distribution for details.
Warranty: Distributed WITHOUT WARRANTY OF ANY KIND

// Unfortunately, the implicitly generated name for a collecting () call
// has to be antigygienic so that it can be referred to by name in more
// has to be unhygienic so that it can be referred to by name in more
// than one macro.

define macro collecting
Expand All @@ -28,6 +28,10 @@ define macro collecting
?body;
collected(?=_collector)
end }
// This variant could have returned values(collected(var1), collected(var2), ...)
// to match the way unnamed collections work, but unfortunately that would break
// current callers if it were changed now. (The callers I checked in OD would be
// trivial to convert.)
{ collecting (?vars) ?:body end }
=> { ?vars;
?body }
Expand Down Expand Up @@ -84,6 +88,8 @@ define macro collect-into
end macro;

define macro collected
{ collected () }
=> { collected(?=_collector) }
{ collected (?:name) }
=> { ?name ## "-collection"(?name ## "-collector") }
end macro;
Expand Down
12 changes: 8 additions & 4 deletions sources/collections/collectors.dylan
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ define open generic collector-protocol (class, #key)
add-sequence-last :: <function>,
collection :: <function>);

//// Default.
//// By default collect into a <list>.

define inline sealed method collector-protocol
(class == <object>, #rest options, #key)
Expand Down Expand Up @@ -65,12 +65,16 @@ define inline sealed method collector-protocol
add-sequence-last :: <function>,
collection :: <function>)
values(begin
let head-pair = pair(#f, #());
head(head-pair) := head-pair;
let head-pair = pair(#f, #()); // The collector is #(final-pair . collection)
head(head-pair) := head-pair; // except when the collection is empty.
end,
method (collector :: <pair>, value)
let new-pair = pair(value, collector.tail);
let t = collector.tail;
let new-pair = pair(value, t);
collector.tail := new-pair;
if (empty?(t))
collector.head := new-pair;
end;
value;
end,
method (collector :: <pair>, value)
Expand Down
2 changes: 1 addition & 1 deletion sources/collections/tests/collections-test-suite-app.dylan
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module: collections-test-suite-app
run-test-application(collections-test-suite);
run-test-application();
1 change: 1 addition & 0 deletions sources/collections/tests/collections-test-suite.lid
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Files: library
bit-count
bit-set-tests
table-extensions
collectors
collections-test-suite
Copyright: Original Code is Copyright (c) 1995-2004 Functional Objects, Inc.
All rights reserved.
Expand Down
79 changes: 79 additions & 0 deletions sources/collections/tests/collectors.dylan
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
Module: collections-test-suite


define test test-collect ()
assert-instance?(<list>, collecting() end, "default collection type is <list>?");
assert-equal(#(1, 2, 3),
collecting ()
collect(1);
collect(2);
collect(3);
end,
"items are added at the end by default for lists?");
assert-equal(#(2, 1, 3),
collecting ()
collect(1);
collect-first(2);
collect-last(3);
end,
"collect-first adds to the beginning of the collection?");
let c = collecting ()
collect(1);
collect(2);
collect-first(3);
assert-equal(#(3, 1, 2), collected(),
"collected() works for unnamed collections?");
collect(5);
end;
assert-equal(#(3, 1, 2, 5), c, "collecting returns the collection?");
end test;

// Note that for named collections the collection isn't automatically returned from the
// body of `collecting`, unlike for unnamed collections. See comment in the `collecting`
// macro.
define test test-collect-into ()
assert-equal(#(1, 2, 3),
collecting (c)
collect-into(c, 1);
collect-into(c, 2);
collect-into(c, 3);
collected(c)
end,
"items are added at the end by default?");
assert-equal(#(2, 1, 3),
collecting (c)
collect-into(c, 1);
collect-first-into(c, 2);
collect-last-into(c, 3);
collected(c)
end,
"collect-first-into adds to the beginning of the collection?");
let cc = collecting (c)
collect-first-into(c, 1); // first/last should not matter here
assert-equal(#(1), collected(c));
collect-last-into(c, 2);
assert-equal(#(1, 2), collected(c));
collect-first-into(c, 3);
assert-equal(#(3, 1, 2), collected(c),
"collected(c) works for named collections?");
collect-last-into(c, 5);
collected(c)
end;
assert-equal(#(3, 1, 2, 5), cc, "collecting returns the collection?");
end test;

define test test-collecting-as ()
let c = collecting (as <vector>)
collect(1);
collect(2);
end;
assert-instance?(<vector>, c);
assert-equal(#[1, 2], c);

let c = collecting (as <integer>)
collect(1);
collect(2);
end;
assert-equal(c, 3);
end test;

Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ define module file-source-records-implementation
use threads;
use locators;
// Probably don't need all this, sort it out later
// use collectors;
use byte-vector;
use set;
use streams;
Expand Down
1 change: 0 additions & 1 deletion sources/lib/source-records/source-records-library.dylan
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@ define module source-records-implementation
use common-dylan;
use threads;
// Probably don't need all this, sort it out later
// use collectors;
use set;
use byte-vector;
use streams;
Expand Down
3 changes: 0 additions & 3 deletions sources/project-manager/projects/projects-library.dylan
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ define library projects
use release-info;
use dood; //---*** andrewa: just for with-walk-progress
// Probably don't need all this, sort it out later...
use collections;
use io;
use system;

Expand Down Expand Up @@ -150,8 +149,6 @@ define module projects-implementation
import: { \with-walk-progress };

// Probably don't need all this, sort it out later...
use collectors;
use set;
use locators;
use streams;
use format;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ Warranty: Distributed WITHOUT WARRANTY OF ANY KIND
define library registry-projects
use dylan;
// Probably don't need all this, sort it out later...
use collections;
use io;
use system;
use build-system;
Expand All @@ -26,8 +25,6 @@ define module registry-projects-internal
use dylan-extensions;
use simple-debugging;
// Probably don't need all this, sort it out later...
use collectors;
use set;
use streams;
use locators;
use format;
Expand Down

0 comments on commit d10a340

Please sign in to comment.