2133 lines
79 KiB
HTML
2133 lines
79 KiB
HTML
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN""http://www.w3.org/TR/REC-html40/loose.dtd">
|
|
<html>
|
|
<head>
|
|
<title>SRFI 42: Eager Comprehensions</title>
|
|
</head>
|
|
|
|
<body>
|
|
|
|
<H1>Title</H1>
|
|
|
|
SRFI 42: Eager Comprehensions
|
|
|
|
<H1>Author</H1>
|
|
|
|
Sebastian Egner
|
|
|
|
<H1>Status</H1>
|
|
|
|
<P>This SRFI is currently in ``final'' status. To see an explanation of
|
|
each status that a SRFI can hold, see <a
|
|
href="http://srfi.schemers.org/srfi-process.html">here</a>.
|
|
To comments
|
|
this SRFI, please mail to
|
|
<a href="mailto:srfi-42@srfi.schemers.org">
|
|
<code>srfi-42@srfi.schemers.org</code></a>.
|
|
See <a href="http://srfi.schemers.org/srfi-list-subscribe.html">
|
|
instructions here</a> to subscribe to the list. You can access
|
|
the discussion via
|
|
<a href="http://srfi.schemers.org/srfi-42/mail-archive/maillist.html">
|
|
the archive of the mailing list</a>.
|
|
You can access
|
|
post-finalization messages via
|
|
<a href="http://srfi.schemers.org/srfi-42/post-mail-archive/maillist.html">
|
|
the archive of the mailing list</a>.
|
|
</P>
|
|
<UL>
|
|
<LI>Received: 2003/02/20
|
|
<LI>Draft: 2003/02/20-2003/04/20
|
|
<LI>Revised: 2003/07/07
|
|
<LI>Final: 2003/07/07
|
|
</UL>
|
|
|
|
<H1>Abstract</H1>
|
|
|
|
This SRFI defines a modular and portable mechanism for
|
|
eager comprehensions extending the algorithmic language Scheme
|
|
<a href="#R5RS">[R5RS]</a>.
|
|
An eager comprehension is a convenient notation
|
|
for one or more nested or parallel loops generating
|
|
a sequence of values, and accumulating this sequence into a result.
|
|
In its most simple form, a comprehension according to this SRFI
|
|
looks like this:
|
|
|
|
<a name="example1"></a><blockquote><pre><code>(<a href="#list-ec">list-ec</a> (<a href="#:">:</a> i 5) (* i i)) => (0 1 4 9 16)</code>.</pre></blockquote>
|
|
|
|
Here, <code>i</code> is a local variable that is sequentially
|
|
bound to the values 0, 1, ..., 4, and the squares of these
|
|
numbers are collected in a list.
|
|
The following example illustrates most conventions of this SRFI
|
|
with respect to nesting and syntax:
|
|
|
|
<a name="example1"></a><blockquote><pre><code>(<a href="#list-ec">list-ec</a> (<a href="#:">:</a> n 1 4) (<a href="#:">:</a> i n) (list n i)) => ((1 0) (2 0) (2 1) (3 0) (3 1) (3 2))</code>.</pre></blockquote>
|
|
|
|
In the example, the variable <code>n</code> is first bound to 1
|
|
then to 2 and finally to 3, and for each binding of <code>n</code>
|
|
the variable <code>i</code> is bound to the values
|
|
0, 1, ..., <code>n</code>-1 in turn.
|
|
The expression <code>(list n i)</code> constructs a two-element list for
|
|
each bindings, and the comprehension <code>list-ec</code> collects
|
|
all these results in a list.<P>
|
|
|
|
The mechanism defined in this SRFI has the following properties:
|
|
|
|
<UL>
|
|
|
|
<LI>
|
|
The set of comprehensions defined for this SRFI is inspired by
|
|
those procedures and macros of <a href="#R5RS">[R5RS, 6.]</a>
|
|
leading to natural comprehensions such as
|
|
<a href="#list-ec"><code>list-ec</code></a>,
|
|
<a href="#append-ec"><code>append-ec</code></a>,
|
|
<a href="#sum-ec"><code>sum-ec</code></a>,
|
|
<a href="#min-ec"><code>min-ec</code></a>,
|
|
<a href="#every?-ec"><code>every?-ec</code></a>,
|
|
<a href="#do-ec"><code>do-ec</code></a>, and others.
|
|
Some other natural comprehensions (e.g. <code>gcd-ec</code>) have
|
|
not been included into this SRFI due to their low significance
|
|
for most applications.
|
|
On the other hand, a few comprehensions
|
|
(<a href="#fold-ec"><code>fold-ec</code></a>,
|
|
<a href="#fold3-ec"><code>fold3-ec</code></a>)
|
|
not inspired by <a href="#R5RS">[R5RS]</a> have been
|
|
included due to their broad applicability.
|
|
</LI>
|
|
|
|
<LI>
|
|
There are typed generators
|
|
(<a href="#:list"><code>:list</code></a>, <a href="#:list"><code>:string</code></a>, ...)
|
|
expecting certain types of objects for their arguments.
|
|
These generators usually produce code as efficient as
|
|
hand coded <code>do</code>-loops.
|
|
</LI>
|
|
|
|
<LI>
|
|
There is also the special generator <a href="#:"><code>:</code></a>
|
|
(read 'run through')
|
|
dispatching on the value of its argument list at runtime.
|
|
In the examples above, one or two integers were used to define a range.
|
|
The convenience of omitting the type comes at a certain
|
|
performance penalty, both per iteration and during startup of the loop.
|
|
</LI>
|
|
|
|
<LI>
|
|
Generators can be nested depth-first (as in the example above),
|
|
run in parallel (with an optional <a href="#vars">index variable</a>
|
|
or more generally with <a href="#:parallel"><code>:parallel</code></a>),
|
|
and can be stopped early before (<a href="#:while"><code>:while</code></a>)
|
|
or after (<a href="#:until"><code>:until</code></a>)
|
|
producing the current value.
|
|
</LI>
|
|
|
|
<LI>
|
|
The sequence of values can be filtered
|
|
(<a href="#if"><code>if</code></a>, <a href="#not"><code>not</code></a>,
|
|
<a href="#and"><code>and</code></a>, <a href="#or"><code>or</code></a>),
|
|
intermediate commands can be evaluated between
|
|
generators (<a href="#begin"><code>begin</code></a>), and
|
|
intermediate variables can be introduced (<a href="#:let"><code>:let</code></a>).
|
|
</LI>
|
|
<LI>
|
|
The mechanism is fully modular.
|
|
This means that no existing macro or procedure needs
|
|
to be modified when adding
|
|
<a href="#foo-ec">application-specific comprehensions</a>,
|
|
<a href="#:foo">application-specific typed generators</a>,
|
|
or <a href="#:dfoo">application-specific dispatching generators</a>.
|
|
</LI>
|
|
|
|
<LI>
|
|
Syntactically, this SRFI uses the
|
|
[<I>outer</I> .. <I>inner</I> | <I>expr</I>] convention,
|
|
meaning that the most right generator (<I>inner</I>) spins fastest
|
|
and is followed by the result expression over which the
|
|
comprehension ranges (<I>expr</I>).
|
|
Refer to the <a href="#convention">Section "Design Rationale"</a>.
|
|
Moreover, the syntax is strictly prefix and the naming
|
|
convention <code>my-comprehension-ec</code>,
|
|
<code>:my-generator</code> is used systematically.
|
|
</LI>
|
|
|
|
</UL>
|
|
|
|
<P>
|
|
|
|
The remainder of this document is organized as follows.
|
|
In section <a href="#rationale">Rationale</a> a brief
|
|
introduction is given what the motivation is for this SRFI.
|
|
The following section <a href="#specification">Specification</a>
|
|
defines the mechanism.
|
|
Section <a href="#ext-examples">Suggestions for
|
|
Application-specific Extensions</a> presents some ideas
|
|
how extensions using this SRFI could look like.
|
|
The section <a href="#design">Design Rationale</a> contains
|
|
some considerations that went into the design of the
|
|
mechanism as defined in the specification.
|
|
The following section <a href="#related-work">Related Work
|
|
and Acknowledgements</a> briefly reviews other proposals
|
|
related to Scheme and comprehensions or loops.
|
|
Finally, the section <a href="#refimpl">Reference
|
|
Implementation</a> gives source code for a reference
|
|
implementation together with a collection of runnable
|
|
examples and a few examples on extensions.
|
|
|
|
|
|
<H1><a name="rationale"></a>Rationale</H1>
|
|
|
|
The purpose of this SRFI is to provide a compact notation
|
|
for many common cases of loops, in particular those
|
|
constructing values for further computation.
|
|
The origin of this SRFI is my frustration that
|
|
there is no simple for the list of integers
|
|
from 0 to <I>n</I>-1.
|
|
With this SRFI it is <code>(list-ec (: i n) i)</code>.
|
|
Refer to the collection of examples for the
|
|
<a href="#refimpl">reference implementation</a>
|
|
what it can be used for, and what it should not be used for.
|
|
To give a practically useful example,
|
|
the following procedure computes the sorted
|
|
list of all prime numbers below a certain bound
|
|
(you may want to run it yourself to get an idea of
|
|
its efficiency):
|
|
|
|
<blockquote><pre><code>
|
|
(define (eratosthenes n) ; primes in {2..n-1} for n >= 1
|
|
(let ((p? (make-string n #\1)))
|
|
(do-ec (:range k 2 n)
|
|
(if (char=? (string-ref p? k) #\1))
|
|
(:range i (* 2 k) n k)
|
|
(string-set! p? i #\0) )
|
|
(list-ec (:range k 2 n) (if (char=? (string-ref p? k) #\1)) k) ))</code></pre></blockquote>
|
|
|
|
|
|
Apart from <I>make simple things simple</I>,
|
|
there is no other paradigm involved for this SRFI.
|
|
In particular, it is not the ambition to implement the
|
|
powerful <I>lazy</I> list comprehensions of other functional
|
|
programming languages in Scheme.
|
|
If you are looking for that you may want to refer to
|
|
<a href="#SRFI40">[SRFI 40]</a>.
|
|
(The usual definition of the stream of all primes does
|
|
in fact also use Eratosthenes' sieve method.
|
|
It is instructive to compare.)<P>
|
|
|
|
The main focus of the design of this SRFI is portability under
|
|
<a href="#R5RS">[R5RS]</a> and modularity for extension.
|
|
Portability is achieved by limiting the features included.
|
|
Modularity for generators is achieved by a special
|
|
implementation technique using <I>Continuation
|
|
Passing Style</I> for macros (which I learned from
|
|
Richard Kelsey's implementation of "Macros for writing loops",
|
|
<a href="#MWL">[MWL]</a>) and by limiting the
|
|
expressive power of generators.
|
|
Modularity for comprehensions requires no additional effort.
|
|
As modularity was a major design goal, I hope many people will
|
|
find it easy to define their own comprehensions and generators.
|
|
As a starting point for doing so, I have included several
|
|
<a href="#ext-examples">suggestions</a> for extensions.<P>
|
|
|
|
For a detailed motivation of the design decisions,
|
|
please refer to the <a href="#design">Section "Design Rationale"</a>.<P>
|
|
|
|
<H1><a name="specification"></a>Specification</H1>
|
|
|
|
A comprehensions is a hygienic referentially transparent macro
|
|
in the sense of <a href="#R5RS">[R5RS, 4.3.]</a>.
|
|
The macros extend the <code><expression></code>-syntax
|
|
defined in <a href="#R5RS">[R5RS, 7.1.3.]</a>.
|
|
The main syntactic pattern used for defining a comprehension is
|
|
<code><qualifier></code>, representing a generator or a filter.
|
|
It is defined in <a href="#qualifiers">Section "Qualifiers"</a>.<P>
|
|
|
|
The most important instances of <code><qualifier></code> are generators.
|
|
These are defined in <a href="#generators">Section "Generators"</a>.
|
|
Generators come in three flavors,
|
|
as <I>typed generators</I>
|
|
(<a href="#:list"><code>:list</code></a>,
|
|
<a href="#:range"><code>:range</code></a> etc.),
|
|
as <I>the dispatching generator</I> <a href="#:"><code>:</code></a>
|
|
(pronounced as 'run through'),
|
|
and as combined and modified generators
|
|
(<a href="#:parallel"><code>:parallel</code></a>,
|
|
<a href="#:while"><code>:while</code></a>,
|
|
<a href="#:until"><code>:until</code></a>).
|
|
Most generators in this SRFI also support an optional
|
|
<a href="#vars">index variable</a> counting the
|
|
values being generated.<P>
|
|
|
|
Finally, it is explained how to add a new
|
|
<a href="#foo-ec">application-specific comprehension</a>,
|
|
how to add a new <a href="#:foo">application-specific typed generator</a>,
|
|
and how to add a new
|
|
<a href="#:dfoo">application-specific dispatching generator</a>.
|
|
As this concerns code unknown at the time this is being written,
|
|
the explanation should not be taken as
|
|
a <I>specification</I> in the literal sense.
|
|
It rather suggests a convention to follow in order to
|
|
ensure new comprehensions and generators blend seamlessly with
|
|
the ones defined in this SRFI.<P>
|
|
|
|
|
|
<H2><a name="comprehensions"></a>Comprehensions</H2>
|
|
|
|
<DL>
|
|
<DT><a name="do-ec"></a>
|
|
<code>(do-ec <qualifier></code>*<code> <command>)</code>
|
|
</DT>
|
|
|
|
<DD>
|
|
Evaluates the <code><command></code> exactly once
|
|
for each binding in the sequence defined by the qualifiers.
|
|
If there are no qualifiers <code><command></code>
|
|
is evaluated exactly once.
|
|
The expression is evaluated for its side-effects only.
|
|
The result of the comprehension is unspecified.
|
|
</DD>
|
|
</DL>
|
|
|
|
<DL>
|
|
<DT><a name="list-ec"></a>
|
|
<code>(list-ec <qualifier></code>*<code> <expression>)</code>
|
|
</DT>
|
|
|
|
<DD>
|
|
The list of values obtained by evaluating
|
|
<code><expression></code> once for each binding in the sequence
|
|
defined by the qualifiers.
|
|
If there are no qualifiers the result is the list with
|
|
the value of <code><expression></code>.
|
|
</DD>
|
|
</DL>
|
|
|
|
<DL>
|
|
<DT><a name="append-ec"></a>
|
|
<code>(append-ec <qualifier></code>*<code> <expression>)</code>
|
|
</DT>
|
|
|
|
<DD>
|
|
The list obtained by appending all values of <code><expression></code>,
|
|
which must all be lists.<BR>
|
|
Think of it as
|
|
<code>(apply append (list-ec <qualifier></code>*<code> <expression>))</code>.
|
|
</DD>
|
|
</DL>
|
|
|
|
<DL>
|
|
<DT><a name="string-ec"></a>
|
|
<code>(string-ec <qualifier></code>*<code> <expression>)</code>
|
|
</DT>
|
|
|
|
<DD>
|
|
The string of all values of <code><expression></code>.<BR>
|
|
Think of it as
|
|
<code>(list->string (list-ec <qualifier></code>*<code> <expression>))</code>.
|
|
</DD>
|
|
</DL>
|
|
|
|
<DL>
|
|
<DT><a name="string-append-ec"></a>
|
|
<code>(string-append-ec <qualifier></code>*<code> <expression>)</code>
|
|
</DT>
|
|
|
|
<DD>
|
|
The string obtained by appending all values of <code><expression></code>,
|
|
which must all be strings.<BR>
|
|
Think of it as
|
|
<code>(apply string-append (list-ec <qualifier></code>*<code> <expression>))</code>.
|
|
</DD>
|
|
</DL>
|
|
|
|
<DL>
|
|
<DT><a name="vector-ec"></a>
|
|
<code>(vector-ec <qualifier></code>*<code> <expression>)</code>
|
|
</DT>
|
|
|
|
<DD>
|
|
The vector of all values of <code><expression></code>.<BR>
|
|
Think of it as
|
|
<code>(list->vector (list-ec <qualifier></code>*<code> <expression>))</code>.
|
|
</DD>
|
|
</DL>
|
|
|
|
<DL>
|
|
<DT><a name="vector-of-length-ec"></a>
|
|
<code>(vector-of-length-ec <k> <qualifier></code>*<code> <expression>)</code>
|
|
</DT>
|
|
|
|
<DD>
|
|
The vector of all values of <code><expression></code>,
|
|
of which there must be exactly <code><k></code>.
|
|
This comprehension behaves like <code>vector-ec</code>
|
|
but can be implemented more efficiently.
|
|
</DD>
|
|
</DL>
|
|
|
|
<DL>
|
|
<DT><a name="sum-ec"></a>
|
|
<code>(sum-ec <qualifier></code>*<code> <expression>)</code>
|
|
</DT>
|
|
|
|
<DD>
|
|
The sum of all values of <code><expression></code>.<BR>
|
|
Think of it as
|
|
<code>(apply + (list-ec <qualifier></code>*<code> <expression>))</code>.
|
|
</DD>
|
|
</DL>
|
|
|
|
<DL>
|
|
<DT><a name="product-ec"></a>
|
|
<code>(product-ec <qualifier></code>*<code> <expression>)</code>
|
|
</DT>
|
|
|
|
<DD>
|
|
The product of all values of <code><expression></code>.<BR>
|
|
Think of it as
|
|
<code>(apply * (list-ec <qualifier></code>*<code> <expression>))</code>.
|
|
</DD>
|
|
</DL>
|
|
|
|
<DL>
|
|
<DT><a name="min-ec"></a><a name="max-ec"></>
|
|
<code>(min-ec <qualifier></code>*<code> <expression>)</code><BR>
|
|
<code>(max-ec <qualifier></code>*<code> <expression>)</code>
|
|
</DT>
|
|
|
|
<DD>
|
|
The minimum and maximum of all values of <code><expression></code>.
|
|
The sequence of values must be non-empty.
|
|
Think of these as<BR>
|
|
<code>(apply min (list-ec <qualifier></code>*<code> <expression>))</code><BR>
|
|
<code>(apply max (list-ec <qualifier></code>*<code> <expression>))</code>.<P>
|
|
|
|
If you want to return a default value in case the sequence is empty
|
|
you may want to consider<BR>
|
|
<code>(fold3-ec 'infinity <qualifier></code>*<code> <expression> min min)</code>.
|
|
</DD>
|
|
</DL>
|
|
|
|
<DL>
|
|
<DT><a name="any?-ec"></a>
|
|
<code>(any?-ec <qualifier></code>*<code> <test>)</code>
|
|
</DT>
|
|
|
|
<DD>
|
|
Tests whether any value of <code><test></code> in the sequence
|
|
of bindings specified by the qualifiers is non-<code>#f</code>.
|
|
If this is the case, <code>#t</code> is returned, otherwise <code>#f</code>.
|
|
If there are no bindings in the sequence specified by the qualifiers
|
|
at all then the result is <code>#f</code>.
|
|
The enumeration of values stops after the first non-<code>#f</code>
|
|
encountered.
|
|
</DD>
|
|
</DL>
|
|
|
|
<DL>
|
|
<DT><a name="every?-ec"></a>
|
|
<code>(every?-ec <qualifier></code>*<code> <test>)</code>
|
|
</DT>
|
|
|
|
<DD>
|
|
Tests whether all values of <code><test></code> are non-<code>#f</code>.
|
|
If this is the case, <code>#t</code> is returned, otherwise <code>#f</code>.
|
|
If the sequence is empty the result is <code>#t</code>.
|
|
Enumeration stops after the first <code>#f</code>.
|
|
</DD>
|
|
</DL>
|
|
|
|
<DL>
|
|
<DT><a name="first-ec"></a><a name="last-ec"></a><code>
|
|
(first-ec <default> <qualifier></code>*<code> <expression>)<br>
|
|
(last-ec <default> <qualifier></code>*<code> <expression>)</code>
|
|
</DT>
|
|
|
|
<DD>
|
|
The first or last value of <code><expression></code>
|
|
in the sequence of bindings specified by the qualifiers.
|
|
Before enumeration, the result is initialized
|
|
with the value of <code><default></code>;
|
|
so this will be the result if the sequence is empty.
|
|
Enumeration is terminated in <code>first-ec</code>
|
|
when the first value has been computed.
|
|
</DD>
|
|
</DL>
|
|
|
|
<DL>
|
|
<DT><a name="fold-ec"></a><a name="fold3-ec"></a><code>
|
|
(fold-ec <x0> <qualifier></code>*<code> <expression> <f2>)<br>
|
|
(fold3-ec <x0> <qualifier></code>*<code> <expression> <f1> <f2>)</code>
|
|
</DT>
|
|
|
|
<DD>
|
|
Reduces the sequence <I>x</I>[0], <I>x</I>[1], ..., <I>x</I>[<I>n</I>-1]
|
|
of values obtained by evaluating <code><expression></code> once
|
|
for each binding as specified by <code><qualifier></code>*.
|
|
The arguments <code><x0></code>, <code><f2></code>,
|
|
and <code><f1></code>, all syntactically equivalent to
|
|
<code><expression></code>, specify the reduction process.<P>
|
|
|
|
The reduction process for <code>fold-ec</code> is defined as follows.
|
|
A reduction variable <code>x</code> is initialized to the value
|
|
of <code><x0></code>,
|
|
and for each <I>k</I> in {0, ..., <I>n</I>-1} the command
|
|
<code>(set! x (<f2> </code><I>x</I>[<I>k</I>]<code> x))</code>
|
|
is evaluated.
|
|
Finally, <code>x</code> is returned as the value of the comprehension.<P>
|
|
|
|
The reduction process for <code>fold3-ec</code> is different.
|
|
If and only if <I>n</I> = 0, i.e. the sequence is empty, then
|
|
<code><x0></code> is evaluated and returned as the value
|
|
of the comprehension.
|
|
Otherwise, a reduction variable <code>x</code> is initialized
|
|
to the value of <code>(<f1> </code><I>x</I>[0]<code>)</code>,
|
|
and for each <I>k</I> in {1, ..., <I>n</I>-1} the command
|
|
<code>(set! x (<f2> </code><I>x</I>[<I>k</I>]<code> x))</code>
|
|
is evaluated.
|
|
Finally, <code>x</code> is returned as the value of the comprehension.<P>
|
|
|
|
As the order of the arguments suggests,
|
|
<code><x0></code> is evaluated outside the scope of the
|
|
qualifiers, whereas the reduction expressions involving
|
|
<code><f1></code> and <code><f2></code> are
|
|
inside the scope of the qualifiers (so they may depend on
|
|
any variable introduced by the qualifiers).
|
|
Note that <code><f2></code> is evaluated repeatedly,
|
|
with any side-effect or overhead this might have.<P>
|
|
|
|
The main purpose of these comprehensions is
|
|
implementing other comprehensions as special cases.
|
|
They are generalizations of the procedures
|
|
<code>fold</code> and <code>reduce</code> in the sense of
|
|
<a href="#SRFI1">[SRFI 1]</a>.
|
|
(Concerning naming and argument order, please refer to
|
|
the discussion archive of SRFI 1, in particular
|
|
the posting <a href="#Folds">[Folds]</a>.)
|
|
Note that <code>fold3-ec</code> is defined such that
|
|
<code><x0></code> is only evaluated in case the
|
|
sequence is empty.
|
|
This allows raising an error for the empty sequence,
|
|
as in the example definition of <code>min-ec</code> below.
|
|
</DD>
|
|
</DL>
|
|
|
|
<DL>
|
|
<DT><a name="foo-ec"></a>
|
|
<code><application-specific comprehension></code>
|
|
</DT>
|
|
|
|
<DD>
|
|
An important aspect of this SRFI is a modular mechanism
|
|
to define application-specific comprehensions.
|
|
To create a new comprehension a hygienic macro
|
|
of this name is defined.
|
|
The macro transforms the new comprehension patterns
|
|
into instances of <a href="#do-ec"><code>do-ec</code></a>, which is the most
|
|
fundamental eager comprehension, or any other comprehension
|
|
already defined.
|
|
For example, the following code defines
|
|
<a href="#list-ec"><code>list-ec</code></a> and
|
|
<a href="#min-ec"><code>min-ec</code></a>
|
|
in terms of <a href="#fold-ec"><code>fold-ec</code></a>
|
|
and <a href="#fold3-ec"><code>fold3-ec</code></a>:
|
|
|
|
<blockquote><pre><code>
|
|
(define-syntax list-ec
|
|
(syntax-rules ()
|
|
((list-ec etc1 etc ...)
|
|
(reverse (fold-ec '() etc1 etc ... cons)) )))
|
|
|
|
(define-syntax min-ec
|
|
(syntax-rules ()
|
|
((min-ec etc1 etc ...)
|
|
(fold3-ec (min) etc1 etc ... min min) )))</code></pre></blockquote>
|
|
|
|
Note that the pattern <code>etc1 etc ...</code> matches
|
|
the syntax <code><qualifier></code>*<code> <expression></code>
|
|
without separate access to <code><qualifier></code>*<code></code>
|
|
and <code><expression></code>.
|
|
In order to define a comprehension that does need explicit
|
|
access to the <code><expression></code>-part,
|
|
the following method is used.
|
|
First, all qualifiers are collected into a
|
|
<a href="#nested"><code>nested</code></a>-qualifier, and then the
|
|
'exactly one qualifier'-case is implemented.
|
|
For illustration, the following code defines
|
|
<a href="#fold3-ec"><code>fold3-ec</code></a> in terms of
|
|
<a href="#do-ec"><code>do-ec</code></a>:
|
|
|
|
<a name="fold3-ec-example"></a>
|
|
<blockquote><pre><code>
|
|
(define-syntax fold3-ec
|
|
(syntax-rules (nested)
|
|
((fold3-ec x0 (nested q1 ...) q etc1 etc2 etc3 etc ...)
|
|
(fold3-ec x0 (nested q1 ... q) etc1 etc2 etc3 etc ...) )
|
|
((fold3-ec x0 q1 q2 etc1 etc2 etc3 etc ...)
|
|
(fold3-ec x0 (nested q1 q2) etc1 etc2 etc3 etc ...) )
|
|
((fold3-ec x0 expression f1 f2)
|
|
(fold3-ec x0 (nested) expression f1 f2) )
|
|
|
|
((fold3-ec x0 qualifier expression f1 f2)
|
|
(let ((result #f) (empty #t))
|
|
(do-ec qualifier
|
|
(let ((value expression)) ; don't duplicate code
|
|
(if empty
|
|
(begin (set! result (f1 value))
|
|
(set! empty #f) )
|
|
(set! result (f2 value result)) )))
|
|
(if empty x0 result) ))))</code></pre></blockquote>
|
|
|
|
Finally, observe that the newly defined <code>fold3-ec</code>
|
|
comprehension inherits all types of qualifiers supported by
|
|
<code>do-ec</code>, including all application-specific generators;
|
|
no further definitions are necessary.
|
|
</DD>
|
|
</DL>
|
|
|
|
|
|
<H2><a name="qualifiers"></a>Qualifiers</H2>
|
|
|
|
This section defines the syntax <code><qualifier></code>.
|
|
The nesting of qualifiers is from left (outer) to right (inner).
|
|
In other words, the rightmost generator spins fastest.
|
|
The nesting also defines the scope of the variables introduced
|
|
by the generators.
|
|
This implies that inner generators may depend on the variables
|
|
of outer generators.
|
|
The sequence of enumeration of values is strictly <I>depth first</I>.
|
|
These conventions are illustrated by the
|
|
first <a href="#example1">example</a>.<P>
|
|
|
|
The syntax <code><qualifier></code> consists of the following alternatives:<P>
|
|
|
|
|
|
<DL>
|
|
<DT><a name="generator"></a>
|
|
<code><generator></code>
|
|
</DT>
|
|
|
|
<DD>
|
|
Enumerates a sequence of bindings of one or more variables.
|
|
The scope of the variables starts at the generator and extends
|
|
over all subsequent qualifiers and expressions in the comprehension.
|
|
The <code><generator></code>-syntax is defined in
|
|
<a href="#generators">Section "Generators"</a>.
|
|
</DD>
|
|
</DL>
|
|
|
|
<DL>
|
|
<DT><a name="if"></a>
|
|
<code>(if <test>)</code>
|
|
</DT>
|
|
|
|
<DD>
|
|
Filters the sequence of bindings by testing if
|
|
<code><test></code> evaluates to non-<code>#f</code>.
|
|
Only for those bindings for which this is the case,
|
|
the subsequent qualifiers of the comprehension are evaluated.
|
|
</DD>
|
|
</DL>
|
|
|
|
<DL>
|
|
<DT><a name="not"></a><a name="and"></a><a name="or"></a><code>
|
|
(not <test>)<br>
|
|
(and <test></code>*<code>)<br>
|
|
(or <test></code>*<code>)</code>
|
|
</DT>
|
|
|
|
<DD>
|
|
Abbreviated notations for filters of the form
|
|
<code>(if (not <test>))</code>,
|
|
<code>(if (and <test></code>*<code>))</code>, and
|
|
<code>(if (or <test></code>*<code>))</code>.
|
|
These represent frequent cases of filters.
|
|
</DD>
|
|
</DL>
|
|
|
|
<DL>
|
|
<DT><a name="begin"></a>
|
|
<code>(begin <sequence>)</code>
|
|
</DT>
|
|
|
|
<DD>
|
|
Evaluate <code><sequence></code>, consisting of
|
|
<code><command></code>*<code> <expression></code>,
|
|
once for each binding of the variables defined by the
|
|
previous qualifiers in the comprehension.
|
|
Using this qualifier, side effects can be inserted
|
|
into the body of a comprehension.
|
|
</DD>
|
|
</DL>
|
|
|
|
<DL>
|
|
<DT><a name="nested"></a>
|
|
<code>(nested <qualifier></code>*<code>)</code>
|
|
</DT>
|
|
|
|
<DD>
|
|
A syntactic construct to group qualifiers.
|
|
The meaning of a qualifier according to the
|
|
<code>nested</code>-syntax is the same as
|
|
inserting <code><qualifier></code>*
|
|
into the enclosing comprehension.
|
|
This construct can be used to reduce comprehensions with several
|
|
qualifiers into a form with exactly one qualifier.
|
|
</DD>
|
|
</DL>
|
|
|
|
|
|
<H2><a name="generators"></a>Generators</H2>
|
|
|
|
This section defines the syntax <code><generator></code>.
|
|
Each generator defines a sequence of bindings through
|
|
which one or more variables are run.
|
|
The scope of the variables begins after the closing
|
|
parenthesis of the generator expression and extends
|
|
to the end of the comprehension it is part of.<P>
|
|
|
|
<a name="vars"></a>The variables defined by the generators
|
|
are specified using the syntax<P>
|
|
|
|
<blockquote><pre>
|
|
<code><vars> --> <variable1></code> [ <code>(index <variable2>)</code> ],</pre></blockquote>
|
|
|
|
where <code><variable1></code> runs through the values in
|
|
the sequence defined by the generator, and the optional
|
|
<code><variable2></code> is an exact integer-valued
|
|
index variable counting the values (starting from 0).
|
|
The names of the variables must be distinct.
|
|
The following example illustrates the index variable:<P>
|
|
|
|
<blockquote><pre><code>
|
|
(list-ec (: x (index i) "abc") (list x i)) => ((#\a 0) (#\b 1) (#\c 2))</code></pre></blockquote>
|
|
|
|
Unless defined otherwise, all generators make sure that the
|
|
expressions provided for their syntactic arguments are
|
|
evaluated exactly once, before enumeration begins.
|
|
Moreover, it may be assumed that the generators do not
|
|
copy the code provided for their arguments, because that
|
|
could lead to exponential growth in code size.
|
|
Finally, it is possible to assign a value to the variables
|
|
defined by a generator, but the effect of this assignment
|
|
is unspecified.<P>
|
|
|
|
The syntax <code><generator></code> consists of the following alternatives:<P>
|
|
|
|
<DL>
|
|
<DT><a name=":"></a>
|
|
<code>(: <vars> <arg1> <arg></code>*<code>)</code>
|
|
</DT>
|
|
|
|
<DD>
|
|
First the expressions <code><arg1> <arg></code>*
|
|
are evaluated into <I>a</I>[1] <I>a</I>[2] ... <I>a</I>[<I>n</I>]
|
|
and then a global dispatch procedure is used to dispatch on
|
|
the number and types of the arguments and run the resulting
|
|
generator.
|
|
|
|
<a name=":-dispatch"></a>
|
|
Initially (after loading the SRFI),
|
|
the following cases are recognized:<P>
|
|
|
|
<TABLE>
|
|
|
|
<TR>
|
|
<TD><a href="#:list"><code>:list</code></a>
|
|
<TD>if
|
|
<TD>for all <I>i</I> in {1..<I>n</I>}:
|
|
<code>(list? </code><I>a</I>[<I>i</I>]<code>)</code>.
|
|
</TR>
|
|
|
|
<TR>
|
|
<TD><a href="#:string"><code>:string</code></a>
|
|
<TD>if
|
|
<TD>for all <I>i</I> in {1..<I>n</I>}:
|
|
<code>(string? </code><I>a</I>[<I>i</I>]<code>)</code>.
|
|
</TR>
|
|
|
|
<TR>
|
|
<TD><a href="#:vector"><code>:vector</code></a>
|
|
<TD>if
|
|
<TD>for all <I>i</I> in {1..<I>n</I>}:
|
|
<code>(vector? </code><I>a</I>[<I>i</I>]<code>)</code>.
|
|
</TR>
|
|
|
|
<TR>
|
|
<TD><a href="#:range"><code>:range</code></a>
|
|
<TD>if
|
|
<TD><I>n</I> in {1..3} and
|
|
for all <I>i</I> in {1..<I>n</I>}:
|
|
<code>(integer? </code><I>a</I>[<I>i</I>]<code>)</code> and
|
|
<code>(exact? </code><I>a</I>[<I>i</I>]<code>)</code>.
|
|
</TR>
|
|
|
|
<TR>
|
|
<TD><a href="#:real-range"><code>:real-range</code></a>
|
|
<TD>if
|
|
<TD><I>n</I> in {1..3} and
|
|
for all <I>i</I> in {1..<I>n</I>}:
|
|
<code>(real? </code><I>a</I>[<I>i</I>]<code>)</code>.
|
|
</TR>
|
|
|
|
<TR>
|
|
<TD><a href="#:char-range"><code>:char-range</code></a>
|
|
<TD>if
|
|
<TD><I>n</I> = 2 and
|
|
for all <I>i</I> in {1, 2}:
|
|
<code>(char? </code><I>a</I>[<I>i</I>]<code>)</code>.
|
|
</TR>
|
|
|
|
<TR>
|
|
<TD><a href="#:port"><code>:port</code></a>
|
|
<TD>if
|
|
<TD><I>n</I> in {1,2} and
|
|
<code>(input-port? </code><I>a</I>[1]<code>)</code> and
|
|
<code>(procedure? </code><I>a</I>[2]<code>)</code>.
|
|
</TR>
|
|
|
|
</TABLE><P>
|
|
|
|
<a name="make-initial-:-dispatch"></a><a name=":-dispatch-ref"></a><a name=":-dispatch-set!"></a>
|
|
The current dispatcher can be retrieved as
|
|
<code>(:-dispatch-ref)</code>, a new dispatcher <I>d</I>
|
|
can be installed by
|
|
<code>(:-dispatch-set! </code><I>d</I><code>)</code>
|
|
yielding an unspecified result,
|
|
and a copy of the initial dispatcher can be obtained as
|
|
<code>(make-initial-:-dispatch)</code>.
|
|
Please refer to the section <a href="#:dfoo-global">below</a>
|
|
for recommendation how to add cases to the dispatcher.
|
|
</DD>
|
|
</DL>
|
|
|
|
<DL>
|
|
<DT><a name=":list"></a><a name=":string"></a><a name=":vector"></a><code>
|
|
(:list <vars> <arg1> <arg></code>*<code>)<br>
|
|
(:string <vars> <arg1> <arg></code>*<code>)<br>
|
|
(:vector <vars> <arg1> <arg></code>*<code>)</code>
|
|
</DT>
|
|
|
|
<DD>
|
|
Run through one or more lists, strings, or vectors.
|
|
First all expressions in <code><arg1> <arg></code>*
|
|
are evaluated and then all elements of the resulting values
|
|
are enumerated from left to right.
|
|
One can think of it as first appending all arguments and
|
|
then enumerating the combined object.
|
|
As a clarifying example, consider<BR>
|
|
<code>(list-ec (:string c (index i) "a" "b") (cons c i))</code> =>
|
|
<code>((#\a . 0) (#\b . 1))</code>.
|
|
</DD>
|
|
</DL>
|
|
|
|
<DL>
|
|
<DT><a name=":integers"></a>
|
|
<code>(:integers <vars>)</code>
|
|
</DT>
|
|
|
|
<DD>
|
|
Runs through the sequence 0, 1, 2, ... of non-negative integers.
|
|
This is most useful in combination with
|
|
<a href="#:parallel"><code>:parallel</code></a>,
|
|
<a href="#:while"><code>:while</code></a>, and
|
|
<a href="#:until"><code>:until</code></a> or with
|
|
a non-local exit in the body of the comprehension.
|
|
</DD>
|
|
</DL>
|
|
|
|
<DL>
|
|
<DT><a name=":range"></a><code>
|
|
(:range <vars> <stop>)<br>
|
|
(:range <vars> <start> <stop>)<br>
|
|
(:range <vars> <start> <stop> <step>)</code>
|
|
</DT>
|
|
|
|
<DD>
|
|
Runs through a range of exact rational numbers.<P>
|
|
|
|
The form <code>(:range <vars> <stop>)</code>
|
|
evaluates the expression <code><stop></code>,
|
|
which must result in an exact integer <I>n</I>,
|
|
and runs through the finite sequence 0, 1, 2, ..., <I>n</I>-1.
|
|
If <I>n</I> is zero or negative the sequence is empty.<P>
|
|
|
|
The form <code>(:range <vars> <start> <stop>)</code>
|
|
evaluates the expressions <code><start></code> and
|
|
<code><stop></code>,
|
|
which must result in exact integers <I>a</I> and <I>b</I>,
|
|
and runs through the finite sequence
|
|
<I>a</I>, <I>a</I>+1, <I>a</I>+2, ..., <I>b</I>-1.
|
|
If <I>b</I> is less or equal <I>a</I> then the sequence is empty.<P>
|
|
|
|
The form <code>(:range <vars> <start> <stop> <step>)</code>
|
|
first evaluates the expressions <code><start></code>,
|
|
<code><stop></code>, and <code><step></code>,
|
|
which must result in exact integers <I>a</I>, <I>b</I>, and <I>s</I>
|
|
such that <I>s</I> is unequal to zero.
|
|
Then the sequence
|
|
<I>a</I>, <I>a</I> + <I>s</I>, <I>a</I> + 2 <I>s</I>, ..., <I>a</I> + (<I>n</I>-1) <I>s</I>
|
|
is enumerated where <I>n</I> = ceil((<I>b</I>-<I>a</I>)/<I>s</I>).
|
|
In other words, the sequence starts at <I>a</I>, increments by <I>s</I>,
|
|
and stops when the next value would reach or cross <I>b</I>.
|
|
If <I>n</I> is zero or negative the sequence is empty.
|
|
</DD>
|
|
</DL>
|
|
|
|
<DL>
|
|
<DT><a name=":real-range"></a><code>
|
|
(:real-range <vars> <stop>)<br>
|
|
(:real-range <vars> <start> <stop>)<br>
|
|
(:real-range <vars> <start> <stop> <step>)</code>
|
|
</DT>
|
|
|
|
<DD>
|
|
Runs through a range of real numbers using an explicit index variable.
|
|
This form of range enumeration avoids accumulation of rounding errors
|
|
and is the one to use if any of the numbers defining the range is
|
|
inexact, not an integer, or a bignum of large magnitude.<P>
|
|
|
|
Providing default value 0 for <code><start></code> and
|
|
1 for <code><step></code>, the generator first
|
|
evaluates <code><start></code>, <code><stop></code>,
|
|
and <code><step></code>, which must result in reals
|
|
<I>a</I>, <I>b</I>, and <I>s</I> such that
|
|
<I>n</I> = (<I>b</I>-<I>a</I>)/<I>s</I> is also representable
|
|
as a real.
|
|
Then the sequence 0, 1, 2, ... is enumerated while the
|
|
current value <I>i</I> is less than <I>n</I>, and the
|
|
variable in <code><vars></code> is bound to the
|
|
value <I>a</I> + <I>i</I> <I>s</I>.
|
|
If any of the values <I>a</I>, <I>b</I>, or <I>s</I> is
|
|
non-exact then all values in the sequence are non-exact.
|
|
</DD>
|
|
</DL>
|
|
|
|
<DL>
|
|
<DT><a name=":char-range"></a>
|
|
<code>(:char-range <vars> <min> <max>)</code>
|
|
</DT>
|
|
|
|
<DD>
|
|
Runs through a range of characters.
|
|
First <code><min></code> and <code><max></code> are
|
|
evaluated, which must result in two characters <I>a</I> and <I>b</I>.
|
|
Then the sequence of characters
|
|
<I>a</I>, <I>a</I>+1, <I>a</I>+2, ..., <I>b</I>
|
|
is enumerated in the order defined by <code>char<=?</code>
|
|
in the sense of <a href="#R5RS">[R5RS, 6.3.4.]</a>.
|
|
If <I>b</I> is smaller than <I>a</I> then the sequence is empty.
|
|
(Note that <I>b</I> is included in the sequence.)
|
|
</DD>
|
|
</DL>
|
|
|
|
<DL>
|
|
<DT><a name=":port"></a>
|
|
<code>(:port <vars> <port>)</code><BR>
|
|
<code>(:port <vars> <port> <read-proc>)</code>
|
|
</DT>
|
|
|
|
<DD>
|
|
Reads from the port until the eof-object is read.
|
|
Providing the default <code>read</code> for
|
|
<code><read-proc></code>, the generator first
|
|
evaluates <code><port></code> and
|
|
<code><read-proc></code>, which must result
|
|
in an input port <I>p</I> and a procedure <I>r</I>.
|
|
Then the variable is run through the sequence obtained
|
|
by <code>(</code><I>r</I><code> </code><I>p</I><code>)</code>
|
|
while the result does not satisfy <code>eof-object?</code>.
|
|
</DD>
|
|
</DL>
|
|
|
|
<DL>
|
|
<DT><a name=":dispatched"></a>
|
|
<code>(:dispatched <vars> <dispatch> <arg1> <arg></code>*<code>)</code>
|
|
</DT>
|
|
|
|
<DD>
|
|
Runs the variables through a sequence defined by <code><dispatch></code>
|
|
and <code><arg1> <arg></code>*.
|
|
The purpose of <code>:dispatched</code> is implementing
|
|
dispatched generators, in particular the predefined dispatching
|
|
generator <a href="#:"><code>:</code></a>.<P>
|
|
|
|
The working of <code>:dispatched</code> is as follows.
|
|
First <code><dispatch></code> and
|
|
<code><arg1> <arg></code>* are evaluated,
|
|
resulting in a procedure <I>d</I> (the 'dispatcher') and
|
|
the values <I>a</I>[1] <I>a</I>[2] ... <I>a</I>[<I>n</I>].
|
|
Then
|
|
<code>(</code><I>d</I><code> (list </code><I>a</I>[1] <I>a</I>[2] ... <I>a</I>[<I>n</I>] <code>))</code>
|
|
is evaluated, resulting in a value <I>g</I>.
|
|
If <I>g</I> is not a procedure then the dispatcher
|
|
did not recognize the argument list and an error is raised.
|
|
Otherwise the 'generator procedure' <I>g</I> is used
|
|
to run <code><vars></code> through a sequence of values.
|
|
The sequence defined by <I>g</I> is obtained by repeated
|
|
evaluation of <code>(</code><I>g</I> <I>empty</I><code>)</code>
|
|
until the result is <I>empty</I>.
|
|
In other words, <I>g</I> indicates the end of the sequence by
|
|
returning its only argument, for which the caller has provided
|
|
an object distinct from anything <I>g</I> can produce.
|
|
(Generator procedures are state based, they are no such noble things
|
|
as streams in the sense of <a href="#SRFI40">SRFI 40</a>.)<P>
|
|
|
|
<a name=":generator-proc"></a>
|
|
The definition of dispatchers is greatly simplified by the
|
|
macro <code>:generator-proc</code> that constructs a generator
|
|
procedure from a typed generator.
|
|
Let <code>(g var arg1 arg ...)</code> be an instance of
|
|
the <code><generator></code> syntax, for example an
|
|
<a href="#:foo">application-specific typed generator</a>,
|
|
with a single variable <code>var</code> and no index variable.
|
|
Then
|
|
|
|
<blockquote><code>
|
|
(:generator-proc (g arg1 arg ...)) => </code><I>g</I>,</blockquote>
|
|
|
|
where the generator procedure <I>g</I> runs through the list
|
|
<code>(list-ec (g var arg1 arg ...) var)</code>.<P>
|
|
|
|
<a name=":dfoo"></a>
|
|
<I>Adding an application-specific dispatching generator</I>.
|
|
In order to define a new dispatching generator (say <code>:my</code>)
|
|
first a dispatching procedure (say <code>:my-dispatch</code>) is defined.
|
|
The dispatcher will be called with a single (!) argument
|
|
containing the list of all values to dispatch on.
|
|
To enable informative error messages, the dispatcher should
|
|
return a descriptive object (e.g. a symbol for the module name)
|
|
when it is called with the empty list.
|
|
Otherwise (if there is at least one value to dispatch on),
|
|
the dispatcher must either return a generator procedure
|
|
or <code>#f</code> (= no interest).
|
|
As an example, the following skeleton code defines a dispatcher
|
|
similar to the initial dispatcher of <a href="#:"><code>:</code></a>:
|
|
|
|
<pre>
|
|
(define (:my-dispatch args)
|
|
(case (length args)
|
|
((0) 'SRFI-NN)
|
|
((1) (let ((a1 (car args)))
|
|
(cond
|
|
((list? a1)
|
|
(:generator-proc (:list a1)) )
|
|
((string? a1)
|
|
(:generator-proc (:string a1)) )
|
|
<I>...more unary cases...</I>
|
|
(else
|
|
#f ))))
|
|
((2) (let ((a1 (car args)) (a2 (cadr args)))
|
|
(cond
|
|
((and (list? a1) (list? a2))
|
|
(:generator-proc (:list a1 a2)) )
|
|
<I>...more binary cases...</I>
|
|
(else
|
|
#f ))))
|
|
<I>...more arity cases...</I>
|
|
(else
|
|
(cond
|
|
((every?-ec (:list a args) (list? a))
|
|
(:generator-proc (:list (apply append args))) )
|
|
<I>...more large variable arity cases...</I>
|
|
(else
|
|
#f )))))</pre>
|
|
|
|
Once the dispatcher has been defined, the following macro
|
|
implements the new dispatching generator:
|
|
|
|
<pre>
|
|
(define-syntax :my
|
|
(syntax-rules (index)
|
|
((:my cc var (index i) arg1 arg ...)
|
|
(:dispatched cc var (index i) :my-dispatch arg1 arg ...) )
|
|
((:my cc var arg1 arg ...)
|
|
(:dispatched cc var :my-dispatch arg1 arg ...) )))</pre>
|
|
|
|
This method of extension yields complete control of the dispatching process.
|
|
Other modules can only add cases to <code>:my</code>
|
|
if they have access to <code>:my-dispatch</code>.<P>
|
|
|
|
<a name=":dfoo-global"></a>
|
|
<I>Extending the predefined dispatched generator</I>.
|
|
An alternative to adding a new dispatched generators is extending
|
|
the predefined generator <a href="#:"><code>:</code></a>.
|
|
Technically, extending <a href="#:"><code>:</code></a> means
|
|
installing a new global dispatching procedure using
|
|
<a href="#:-dispatch-set!"><code>:-dispatch-set!</code></a>
|
|
as described above.
|
|
|
|
<a name="dispatch-union"></a>
|
|
In most cases, however, the already installed dispatcher should
|
|
be extended by new cases.
|
|
The following procedure is a utility for doing so:
|
|
|
|
<pre>(dispatch-union <I>d1</I> <I>d2</I>) => <I>d</I>,</pre>
|
|
|
|
where the new dispatcher <I>d</I> recognizes the union of the
|
|
cases recognized by the dispatchers <I>d1</I> and <I>d2</I>.
|
|
The new dispatcher always tries both component dispatchers
|
|
and raises an error in case of conflict.
|
|
The identification returned by <code>(</code><I>d</I><code>)</code>
|
|
is the concatenation of the component identifications
|
|
<code>(</code><I>d1</I><code>)</code> and
|
|
<code>(</code><I>d2</I><code>)</code>, enclosed in lists
|
|
if necessary.
|
|
For illustration, consider the following code:
|
|
|
|
<pre>
|
|
(define (example-dispatch args)
|
|
(cond
|
|
((null? args)
|
|
'example )
|
|
((and (= (length args) 1) (symbol? (car args)) )
|
|
(:generator-proc (:string (symbol->string (car args)))) )
|
|
(else
|
|
#f )))
|
|
|
|
(:-dispatch-set! (dispatch-union (:-dispatch-ref) example-dispatch))</pre>
|
|
|
|
After evaluation of this code, the following example will work:
|
|
|
|
<pre>(list-ec (: c 'abc) c) => (#\a #\b #\c)</pre>
|
|
|
|
Adding cases to <a href="#:"><code>:</code></a> is particularly useful
|
|
for frequent cases of interactive input.
|
|
Be warned, however, that the advantage of global extension also carries
|
|
the danger of conflicts, unexpected side-effects, and slow dispatching.
|
|
</DD>
|
|
</DL>
|
|
|
|
<DL>
|
|
<DT><a name=":do"></a><code>
|
|
(:do (<lb></code>*<code>) <ne1?> (<ls></code>*<code>))<br>
|
|
(:do (let (<ob></code>*<code>) <oc></code>*<code>) (<lb></code>*<code>) <ne1?> (let (<ib></code>*<code>) <ic></code>*<code>) <ne2?> (<ls></code>*<code>))</code>
|
|
</DT>
|
|
|
|
<DD>
|
|
Defines a generator in terms of a named-<code>let</code>,
|
|
optionally decorated with inner and outer <code>let</code>s.
|
|
This generator is for defining other generators.
|
|
(In fact, the reference implementation transforms any other
|
|
generator into an instance of fully decorated <code>:do</code>.)
|
|
The generator is a compromise between expressive power
|
|
(more flexible loops) and fixed structure (necessary
|
|
for merging and modifying generators).
|
|
In the fully decorated form, the syntactic variables
|
|
<code><ob></code> (outer binding),
|
|
<code><oc></code> (outer command),
|
|
<code><lb></code> (loop binding),
|
|
<code><ne1?></code> (not-end1?),
|
|
<code><ib></code> (inner binding),
|
|
<code><ic></code> (inner command),
|
|
<code><ne2?></code> (not-end2?), and
|
|
<code><ls></code> (loop step)
|
|
define the following loop skeleton:
|
|
|
|
<pre>
|
|
(let (<ob>*)
|
|
<oc>*
|
|
(let loop (<lb>*)
|
|
(if <ne1?>
|
|
(let (<ib>*)
|
|
<ic>*
|
|
<I>payload</I>
|
|
(if <ne2?>
|
|
(loop <ls>*) ))))),</pre>
|
|
|
|
where <code><oc>*</code> and
|
|
<code><ic>*</code> are syntactically
|
|
equivalent to <code><command></code>*,
|
|
i.e. they do not begin with a <code><definition></code>.
|
|
The latter requirement allows the code generator to
|
|
produce more efficient code for special cases by
|
|
removing empty <code>let</code>-expressions altogether.
|
|
</DD>
|
|
</DL>
|
|
|
|
<DL>
|
|
<DT><a name=":let"></a>
|
|
<code>(:let <vars> <expression>)</code>
|
|
</DT>
|
|
|
|
<DD>
|
|
Runs through the sequence consisting of the value of
|
|
<code><expression></code>, only.
|
|
This is the same as
|
|
<code>(:list <vars> (list <expression>))</code>.
|
|
If an index variable is specified, its value is 0.
|
|
The <code>:let</code>-generator can be used to introduce
|
|
an intermediate variable depending on outer generators.
|
|
</DD>
|
|
</DL>
|
|
|
|
<DL>
|
|
<DT><a name=":parallel"></a>
|
|
<code>(:parallel <generator></code>*<code>)</code>
|
|
</DT>
|
|
|
|
<DD>
|
|
Runs several generators in parallel.
|
|
This means that the next binding in the sequence is obtained
|
|
by advancing each generator in <code><generator></code>*
|
|
by one step.
|
|
The parallel generator terminates when any of its component
|
|
generators terminates.
|
|
The generators share a common scope for the variables
|
|
they introduce.
|
|
This implies that the names of the variables introduced
|
|
by the various generators must be distinct.
|
|
</DD>
|
|
</DL>
|
|
|
|
<DL>
|
|
<DT><a name=":while"></a>
|
|
<code>(:while <generator> <expression>)</code>
|
|
</DT>
|
|
|
|
<DD>
|
|
Runs <code><generator></code> while <code><expression></code>
|
|
evaluates to non-<code>#f</code>.
|
|
The guarding expression is included in the scope
|
|
of the variables introduced by the generator.<P>
|
|
|
|
Note the distinction between the filter <code>if</code> and
|
|
the modified generator expressed by <code>:while</code>.
|
|
</DD>
|
|
</DL>
|
|
|
|
<DL>
|
|
<DT><a name=":until"></a>
|
|
<code>(:until <generator> <expression>)</code>
|
|
</DT>
|
|
|
|
<DD>
|
|
Runs <code><generator></code> until after
|
|
<code><expression></code> has evaluated to non-<code>#f</code>.
|
|
The guarding expression is included in the scope
|
|
of the variables introduced by the generator.<P>
|
|
|
|
Note the distinction between <code>:while</code>, stopping <I>at</I>
|
|
a certain condition, and <code>:until</code>, stopping <I>after</I>
|
|
a certain condition has occurred. The latter implies that the binding
|
|
that has triggered termination has been processed by the comprehension.
|
|
</DD>
|
|
</DL>
|
|
|
|
<DL>
|
|
<DT><a name=":foo"></a>
|
|
<code><application-specific typed generator></code>
|
|
</DT>
|
|
|
|
<DD>
|
|
An important aspect of this SRFI is a modular mechanism to
|
|
define new typed generators.
|
|
To define a new typed generator a hygienic referentially
|
|
transparent macro of the same name is defined to transform
|
|
the generator pattern into an instance of the
|
|
<a href="#:do"><code>:do</code></a>-generator.
|
|
The extension is fully modular, meaning that no other
|
|
macro has to be modified to add the new generator.
|
|
This is achieved by defining the new macro in
|
|
<I>Continuation Passing Style</I>, as in <a href="#MWL">[MWL]</a>.<P>
|
|
|
|
Technically, this works as follows.
|
|
Assume the generator syntax <code>(:mygen <var> <arg>)</code>
|
|
is to be implemented, for example running the variable <code><var></code>
|
|
through the list <code>(reverse <arg>)</code>.
|
|
The following definition implements <code>:mygen</code>
|
|
in terms of <a href="#:list"><code>:list</code></a>
|
|
using the additional syntactic variable <code>cc</code>
|
|
(read <I>current continuation</I>):
|
|
|
|
<pre>
|
|
(define-syntax :mygen
|
|
(syntax-rules ()
|
|
((:mygen cc var arg)
|
|
(:list cc var (reverse arg)) )))</pre>
|
|
|
|
After this definition, any comprehension will accept
|
|
the <code>:mygen</code>-generator and produce the
|
|
proper code for it.
|
|
This works as follows.
|
|
When a comprehension sees something of the form
|
|
<code>(g arg ...)</code> in the position of a
|
|
<code><qualifier></code> then it will
|
|
transform the entire comprehension into
|
|
<code>(g (continue ...) arg ...)</code>.
|
|
This effectively 'transfers control' to the
|
|
macro <code>g</code>, for example <code>:mygen</code>.
|
|
The macro <code>g</code> has full control of
|
|
the transformation, but eventually it should
|
|
transform the expression into
|
|
<code>(:do (continue ...) etc ...)</code>.
|
|
In the <code>:mygen</code>-example this is done
|
|
by the <code>:list</code>-macro.
|
|
The macro <code>:do</code> finally transforms
|
|
into <code>(continue ... (:do etc ...))</code>.
|
|
As <code>continue</code> has been chosen by the
|
|
macro implementing the comprehension,
|
|
it can regain control and proceed
|
|
with other qualifiers.<P>
|
|
|
|
In order to ensure consistency of new generators
|
|
with the ones defined in this SRFI, a few conventions
|
|
are in order.
|
|
Firstly, the generator patterns begin with one or more
|
|
variables followed by arguments defining the sequence.
|
|
Secondly, each generator except <code>:do</code>
|
|
can handle an optional index variable.
|
|
This is most easily implemented using
|
|
<a href="#:parallel"><code>:parallel</code></a>
|
|
together with <a href="#:integers"><code>:integers</code></a>.
|
|
In case the payload generator needs an index anyhow
|
|
(e.g. <a href="#:vector"><code>:vector</code></a>)
|
|
it is more efficient to add an index-variable if
|
|
none is given and to implement the indexed case.
|
|
Finally, make sure that no syntactic variable of the
|
|
generator pattern ever gets duplicated in the code
|
|
(to avoid exponential code size in nested application),
|
|
and introduce sufficient intermediate variables to
|
|
make sure expressions are evaluated at the correct time.
|
|
</DD>
|
|
</DL>
|
|
|
|
<H1><a name="ext-examples"></a>
|
|
Suggestions for application-specific extensions</H1>
|
|
|
|
<H3>Arrays in the sense of <a href="#SRFI25">[SRFI25]</a></H3>
|
|
|
|
In order to create an array from a sequence of elements,
|
|
a comprehension with the following syntax would be useful:
|
|
|
|
<pre>(array-ec <shape> <qualifier>* <expression>).</pre>
|
|
|
|
The comprehension constructs a new array of the given shape
|
|
by filling it row-major with the sequence of elements as specified
|
|
by the qualifiers.
|
|
|
|
On the generator side, it would be most useful to have a
|
|
generator of the form
|
|
|
|
<pre>(:array <vars> <arg>),</pre>
|
|
|
|
running through the elements of the array in row-major.
|
|
For the optional index variable, the extension
|
|
<code>(index <k1> <k></code>*<code>)</code>
|
|
could be defined where <code><k1> <k></code>*
|
|
are variable names indexing the various dimensions.
|
|
|
|
|
|
<H3>Random Numbers in the sense of <a href="#SRFI27">[SRFI27]</a></H3>
|
|
|
|
In order to create a vector or list of random numbers,
|
|
it would be convenient to have generators of the following form:
|
|
|
|
<pre>
|
|
(:random-integer [ <range> [ <number> ] ] )
|
|
(:random-real [ <number> ] )</pre>
|
|
|
|
where <code><range></code> (default 2) indicates the range of
|
|
the integers and <code><number></code> (default infinity)
|
|
specifies how many elements are to be generated.
|
|
Derived from these basic generators, one could define several
|
|
other generators for other distributions (e.g. Gaussian).
|
|
|
|
|
|
<H3>Bitstrings in the sense of <a href="#SRFI33">[SRFI33]</a></H3>
|
|
|
|
As eager comprehensions are efficient, they can be useful
|
|
for operations involving strings of bits.
|
|
It could be useful to have the following comprehension:
|
|
|
|
<pre>(bitwise-ec <qualifier>* <expression>),</pre>
|
|
|
|
which constructs an integer from bits obtained as values
|
|
of <code><expression></code> in the ordering defined
|
|
by <a href="#SRFI33">[SRFI33]</a>.
|
|
In other words, if the sequence of values is
|
|
<I>x</I>[0], <I>x</I>[1], ..., <I>x</I>[<I>n</I>-1] then
|
|
the result is <I>x</I>[0] + <I>x</I>[1] 2 + ... + <I>x</I>[<I>n</I>-1] 2^(<I>n</I>-1).
|
|
On the generator side, a generator of the form
|
|
|
|
<pre>(:bitwise <vars> <arg1> <arg>*)</pre>
|
|
|
|
runs through the sequence of bits obtained by appending the
|
|
binary digits of the integers <code><arg1> <arg></code>*.
|
|
|
|
<H3><a name="srfi40-ec"></a>Streams in the sense of <a href="#SRFI40">[SRFI 40]</a></H3>
|
|
|
|
It is possible to 'lazify' the eager comprehension
|
|
<a href="#list-ec"><code>list-ec</code></a>,
|
|
constructing a stream in the sense of <a href="#SRFI40">[SRFI 40]</a>.
|
|
Clearly, such a comprehension (<code>stream-ec</code>)
|
|
is not eager at all since it only runs the loops when results are requested.
|
|
It is also possible to define a <code>:stream</code>-generator with
|
|
the same API as <a href="#:list"><code>:list</code></a> but running
|
|
through streams instead of lists.<P>
|
|
|
|
For what it is worth,
|
|
the file <a href="http://srfi.schemers.org/srfi-42/srfi40-ec.scm">srfi40-ec.scm</a> implements
|
|
<code>:stream</code> and <code>stream-ec</code> and gives an example.
|
|
The implementation makes substantial use of
|
|
<code>call-with-current-continuation</code> to run the loop
|
|
only when necessary.
|
|
In some implementations of Scheme this may involve
|
|
considerable overhead.
|
|
|
|
<H3>Reading Text Files</H3>
|
|
|
|
Eager comprehensions can also be used to process files.
|
|
However, bear in mind that an eager comprehension wants
|
|
to read and process the entire file right away.
|
|
Nevertheless, these generators would be useful for
|
|
reading through the lines of a file or through the
|
|
characters of a file:
|
|
|
|
<pre>
|
|
(:lines-of-file <vars> <file>)
|
|
(:chars-of-file <vars> [ (line <variable1>) ] [ (column <variable2>) ] <file>)</pre>
|
|
|
|
Here <code><file></code> is either an input port
|
|
or a string interpreted as a filename.
|
|
In a similar fashion, generators reading from sockets defined
|
|
by URLs or other communication facilities could be defined.
|
|
|
|
<H3>The Scheme shell <a href="#SCSH">Scsh</a></H3>
|
|
|
|
In the Scheme-shell Scsh it could be useful to have certain
|
|
comprehensions and generators.
|
|
Candidates for comprehensions are
|
|
<code>begin-ec</code>,
|
|
<code>|-ec</code>,
|
|
<code>||-ec</code>, and
|
|
<code>&&-ec</code>.
|
|
Concerning generators, it might be useful to have
|
|
<code>:directory</code> running through the
|
|
records of a directory, and maybe a
|
|
sophisticated <code>:file-match</code>-generator
|
|
could enumerate file record in a directory structure.
|
|
Optional variables of the generators could give
|
|
convenient access frequent components of the file records
|
|
(e.g. the filename).
|
|
Another candidate is <code>:env</code> to run through
|
|
the environment associations.
|
|
It is left to other authors and other SRFIs to
|
|
define a useful set of comprehensions and generators
|
|
for Scsh.
|
|
|
|
|
|
<H1><a name="design"></a>Design Rationale</H1>
|
|
|
|
<H3>What is the difference between eager and lazy comprehensions?</H3>
|
|
|
|
A lazy comprehension, for example <code>stream-of</code> in the
|
|
sense of <a href="#SRFI40">[SRFI 40]</a>, constructs an object
|
|
representing a sequence of values.
|
|
Only at the time these values are needed that they
|
|
are actually produced.
|
|
An eager comprehension, on the other hand, is an instruction to
|
|
run through a certain sequence of values and do something with it,
|
|
for example as in <a href="#do-ec"><code>do-ec</code></a>.
|
|
In other words, it is nothing more sophisticated than a loop,
|
|
potentially with a more convenient notation.
|
|
This also explains why <code>stream-of</code> is the most
|
|
fundamental <I>lazy</I> comprehension, and all others can
|
|
be formulated in terms of it, whereas the most fundamental
|
|
<I>eager</I> comprehension is <code>do-ec</code>.
|
|
|
|
|
|
<H3><a name="convention"></a>Why the [<I>outer</I> .. <I>inner</I> | <I>expr</I>]
|
|
order of qualifiers?</H3>
|
|
|
|
In principle, there are six possible orders in which the
|
|
qualifiers and the expression of a comprehension can be written.
|
|
We denote the different conventions with a pattern in which
|
|
<I>expr</I> denotes the expression over which the comprehension
|
|
ranges, <I>inner</I> denotes the generator spinning fastest, and
|
|
<I>outer</I> denotes the generator spinning slowest.
|
|
For example, <a href="#Haskell">[Haskell]</a> and
|
|
<a href="#Python">[Python]</a> use
|
|
[<I>expr</I> | <I>outer</I> .. <I>inner</I>].
|
|
(Probably with sufficient persistence, instances for any
|
|
of the conventions can be found on the Internet.)
|
|
In addition, there is the common mathematical notation
|
|
'{<I>f</I>(<I>x</I>) | <I>x</I> in <I>X</I>}'.<P>
|
|
|
|
It is important to understand that the notational convention
|
|
does not only determine the order of enumeration but also the
|
|
scope of the variables introduced by the generators.
|
|
The scope of <I>inner</I> includes <I>expr</I>, and the
|
|
scope of <I>outer</I> should include <I>inner</I> to allow
|
|
inner generates depending on outer generators.
|
|
Eventually, the choice for a particular syntactic convention is
|
|
largely a matter of personal preferences.
|
|
However, there are a few considerations that went into the
|
|
choice made for this SRFI:<P>
|
|
|
|
1. The mathematical notation is universally known and widely used.
|
|
However, the mathematical notation denotes a <I>set</I> comprehension
|
|
in which the order of the qualifiers is either irrelevant or must
|
|
be deduced from the context.
|
|
For the purpose of eager comprehensions as a programming language
|
|
construct, the order does matter and a simple convention is a plus.
|
|
For these reasons, the mathematical notation as such is undesirable,
|
|
but its widespread use is in favor of
|
|
[<I>expr</I> | <I>inner</I> .. <I>outer</I>] and
|
|
[<I>expr</I> | <I>outer</I> .. <I>inner</I>].<P>
|
|
|
|
2. It is desirable to have the scope of the variables increase
|
|
into one direction, as in
|
|
[<I>expr</I> | <I>inner</I> .. <I>outer</I>] and
|
|
[<I>outer</I> .. <I>inner</I> | <I>expr</I>], and
|
|
not change direction, as in
|
|
[<I>expr</I> | <I>outer</I> .. <I>inner</I>]
|
|
where <I>expr</I> is in the scope of <I>inner</I>
|
|
but <I>outer</I> is not.
|
|
This is even more important if the syntax in Scheme
|
|
does not explicitly contain the '|'-separator.<P>
|
|
|
|
3. More complicated comprehensions with several nested generators
|
|
eventually look like nested loops and Scheme always
|
|
introduces them <I>outer</I> .. <I>inner</I> as in
|
|
<code>do</code> and named-<code>let</code>.
|
|
This is in favor of
|
|
[<I>expr</I> | <I>outer</I> .. <I>inner</I>] and
|
|
[<I>outer</I> .. <I>inner</I> | <I>expr</I>].
|
|
Shorter comprehension may look more naturally the
|
|
other way around.<P>
|
|
|
|
Regarding these contradicting preferences, I regard
|
|
linearity in scoping (2.) most important, followed by
|
|
readability for more complicated comprehensions (3.).
|
|
This leads to [<I>outer</I> .. <I>inner</I> | <I>expr</I>].
|
|
An example in Scheme-syntax is
|
|
<code>(list-ec (: x 10) (: y x) (f x y))</code>,
|
|
which looks acceptable to me even without similarity
|
|
to the mathematical notation.
|
|
As a downside, the convention clashes with other the
|
|
convention used in other languages (e.g. Haskell and Python).
|
|
|
|
|
|
<H3>You forgot [<I>choose your favorite here</I>]-ec!</H3>
|
|
|
|
I tried to construct a reasonably useful set of tools
|
|
according to what <a href="#R5RS">[R5RS]</a> specifies.
|
|
Nevertheless, is the choice what to include and what to
|
|
leave out eventually a matter of personal preference.<P>
|
|
|
|
When 'packing the toolbox' I went for travelling light;
|
|
this SRFI does not include everything imaginable
|
|
or even everything useful.
|
|
I oriented myself at the standard procedures
|
|
of <a href="#R5RS">[R5RS]</a>,
|
|
with a few omissions and additions.
|
|
A notable omission are <code>gcd-ec</code> and
|
|
<code>lcm-ec</code> because they are one-liners,
|
|
and more severely, of questionable value in practice.
|
|
A notable addition are
|
|
<a href="#fold-ec"><code>fold-ec</code></a> and
|
|
<a href="#fold3-ec"><code>fold3-ec</code></a>,
|
|
providing a mechanism to define lots of useful one-liners.
|
|
The other notable addition is
|
|
<a href="#first-ec"><code>first-ec</code></a>, which
|
|
is the fundamental 'early stopping' comprehension.
|
|
It is used to define
|
|
<a href="#any?-ec"><code>any?-ec</code></a> and
|
|
<a href="#every?-ec"><code>every?-ec</code></a>
|
|
which are among the most frequent comprehensions.<P>
|
|
|
|
Concerning the generators, the same principle has been used.
|
|
Additions include <a href="#:range"><code>:range</code></a>
|
|
and friends because they are universally needed, and
|
|
<a href="#:dispatched"><code>:dispatched</code></a> which is
|
|
primarily intended for implementing <a href="#:"><code>:</code></a>.
|
|
|
|
|
|
<H3><a name="dovetail"></a>Why is the order of enumeration specified?</H3>
|
|
|
|
For the purpose of this SRFI, every generator runs through
|
|
its sequence of bindings in a well specified order, and nested
|
|
generators run through the Cartesian product in the order
|
|
of nested loops.
|
|
The purpose of this definition is making the sequence as
|
|
easily predictable as possible.
|
|
|
|
On the other hand, many mechanisms for <I>lazy</I> comprehensions
|
|
do not specify the order in which the elements are enumerated.
|
|
When it comes to infinite streams, this has the great advantage
|
|
that a comprehension may interleave an inner and an outer
|
|
enumeration, a method also known as 'dove-tailing' or 'diagonalizing'.
|
|
Interleaving ensures that any value of the resulting stream is
|
|
produced after a finite amount of time, even if one or more
|
|
inner streams are infinite.
|
|
|
|
<H3>Why both typed and dispatching generators?</H3>
|
|
|
|
The reason for typed generators is runtime efficiency.
|
|
In fact, the code produced by <code>:range</code> and others
|
|
will run as fast as a hand-coded <code>do</code>-loop.
|
|
The primary purpose of the dispatching generator is convenience.
|
|
It comes at the price of reduced runtime performance,
|
|
both for loop iteration and startup.
|
|
|
|
<H3>Why the <I>something</I><code>-ec</code> and <code>:</code><I>type</I> naming?</H3>
|
|
|
|
The purpose of the <code>:</code><I>type</I> convention is to keep
|
|
many common comprehensions down to one-liners.
|
|
In my opinion, the fundamental nature of eager comprehensions
|
|
justifies a single character naming convention.
|
|
The <I>something</I><code>-ec</code> convention is primarily intended to
|
|
stay away from the widely used <I>something</I><code>-of</code>.
|
|
It reduces confusion and conflict with related mechanisms.
|
|
|
|
<H3>Why combine variable binding and sequence definition?</H3>
|
|
|
|
The generators of this SRFI do two different things with
|
|
a single syntactic construct: They define a sequence of values
|
|
to enumerate and they specify a variable (within a certain
|
|
scope) to run through that sequence.
|
|
An alternative is to separate the two, for example as it
|
|
has been done in
|
|
<a href="http://srfi.schemers.org/srfi-40/srfi-40.html">SRFI 40</a>.<P>
|
|
|
|
The reason for combining sequence definition and enumeration
|
|
for the purpose of this SRFI is threefold.
|
|
Firstly, sequences of values are not explicitly represented as
|
|
objects in the typed generators; the generators merely
|
|
manipulate an internal state.
|
|
Secondly, this SRFI aims at a most concise notation for
|
|
common comprehensions and reduces syntax to the naked minimum.
|
|
Thirdly, this SRFI aims at the highest possible performance for
|
|
typed generators, which is achieved if the state being manipulated
|
|
is represented by the loop variable itself.
|
|
|
|
<H3>Why is <code>(: <vars>)</code> illegal?</H3>
|
|
|
|
It is reasonable and easy to define <code>(<a href="#:">:</a> <vars>)</code>
|
|
as <code>(<a href="#:integers">:integers</a> <vars>)</code>,
|
|
enumerating the non-negative integers.
|
|
However, it turned out that a frequent mistake in using the
|
|
eager comprehensions is to forget either the variable
|
|
or an argument for the enumeration.
|
|
As this would lead to an infinite loop (not always
|
|
equally pleasant in interactive sessions), it is not allowed.
|
|
|
|
<H3>Why is there no <code>:sequential</code>?</H3>
|
|
|
|
Just like <a href="#:parallel"><code>:parallel</code></a>
|
|
enumerates generators in
|
|
parallel, a <code>:sequential</code> generator could
|
|
enumerate a concatenation of several generator, starting
|
|
the next one when the previous has finished.
|
|
The reason for not having such a qualifier is
|
|
that the generators should use all the same variable name
|
|
and there is no hygienic and referentially transparent
|
|
way of enforcing this (or even knowing the variable).
|
|
|
|
<H3>Why is there no general <code>let</code>-qualifier?</H3>
|
|
|
|
It is easy to add <code>let</code>, <code>let*</code>,
|
|
and <code>letrec</code> as cases to <code><qualifier></code>.
|
|
This would allow more sophisticated local variables
|
|
and expressions than possible with
|
|
<code>(<a href="#:let">:let</a> <vars> <expression>)</code> and
|
|
<code>(<a href="#begin">begin</a> <sequence></code>*<code>)</code>.
|
|
In particular, a local <code><definition></code>
|
|
in the sense of <a href="#R5RS">[R5RS, 7.1.5.]</a> would
|
|
be possible.<P>
|
|
|
|
There are two reasons for not including <code>let</code>
|
|
and friends as qualifiers.
|
|
The first reason concerns readability.
|
|
A qualifier of the form
|
|
<code>(let (<binding spec></code>*<code>) <body>)</code>
|
|
only makes sense if the scope of the new variables ends at the
|
|
end of the comprehension, and <I>not</I> already
|
|
after <code><body></code>.
|
|
The similarity with ordinary <code>let</code>-expressions
|
|
would be very confusing.
|
|
The second reason concerns the design rationale.
|
|
If sophisticated <code>let</code>-qualifiers involving
|
|
recursion or local definitions are needed, it is likely
|
|
that eager comprehensions are being overused.
|
|
In that case it might be better to define a procedure
|
|
for the task.
|
|
So including an invitation to overuse the mechanism would
|
|
be a serious violation of the
|
|
<I>Keep It Simple and Stupid</I> principle.
|
|
|
|
<H3>Why is there no <code>:nested</code> generator?</H3>
|
|
|
|
The specification above defines <a href="#nested"><code>nested</code></a>
|
|
as a qualifier but <a href="#:parallel"><code>:parallel</code></a>
|
|
as a generator.
|
|
In particular, this makes it impossible to make parallel
|
|
generators from nested ones.<P>
|
|
|
|
This design simply reflects an implementability limitation.
|
|
All component generators of <code>:parallel</code> are
|
|
transformed into <a href="#:do"><code>:do</code></a>-generators
|
|
and these can be merged into a parallel generator.
|
|
However, nested generators cannot be merged easily without
|
|
losing the type of the generator,
|
|
which would seriously hurt modularity and performance.
|
|
|
|
<H3>Is <code>any?-ec</code> eager?</H3>
|
|
|
|
Yes, it is still eager because it immediately starts to
|
|
run through the sequence.<P>
|
|
|
|
In fact, the reference implementation makes sure
|
|
<a href="#first-ec"><code>first-ec</code></a>,
|
|
<a href="#any?-ec"><code>any?-ec</code></a>, and
|
|
<a href="#every?-ec"><code>every?-ec</code></a>
|
|
execute efficiently so they can be used conveniently
|
|
as in
|
|
<code>(every?-ec (:list x my-list) (pred? x))</code>.
|
|
|
|
<H3>Why this whole <code>:dispatched</code> business?</H3>
|
|
|
|
It is specified above that <I>the</I> dispatching generator,
|
|
called <a href="#:"><code>:</code></a>, is just a special case
|
|
of <a href="#:dispatched"><code>:dispatched</code></a> using
|
|
a global dispatching procedure.
|
|
Alternatively, a simple fixed global mechanism to extend
|
|
<a href="#:"><code>:</code></a> could have been used.
|
|
This is much simpler but does not support the definition
|
|
of new dispatched generators.<P>
|
|
|
|
The purpose of <a href="#:dispatched"><code>:dispatched</code></a>
|
|
and its utilities
|
|
(<a href="#:generator-proc"><code>:generator-proc</code></a> and
|
|
<a href="#dispatch-union"><code>dispatch-union</code></a>)
|
|
is the following.
|
|
Assume <a href="#:"><code>:</code></a> is to be used inside a
|
|
module but it is essential that no other module can spoil it,
|
|
e.g. by installing a very slow dispatcher.
|
|
The recommended way to proceed in this case is to define a
|
|
local copy of the original dispatching generator
|
|
<a href="#:"><code>:</code></a>,
|
|
for example with the following code
|
|
|
|
<pre>
|
|
(define :my-dispatch
|
|
(make-initial-:-dispatch) )
|
|
|
|
(define-syntax :my
|
|
(syntax-rules (index)
|
|
((:my cc var (index i) arg1 arg ...)
|
|
(:dispatched cc var (index i) :my-dispatch arg1 arg ...) )
|
|
((:my cc var arg1 arg ...)
|
|
(:dispatched cc var :my-dispatch arg1 arg ...) ))),</pre>
|
|
|
|
and to use the new generator <code>:my</code> instead of
|
|
<a href="#:"><code>:</code></a>.<P>
|
|
|
|
An alternative for the dispatching mechanism as defined in
|
|
this SRFI is the use of parameter objects in the sense of
|
|
<a href="#SRFI39">[SRFI 39]</a>.
|
|
The dispatching generator would then access a dynamically
|
|
scoped variable to find the dispatcher, allowing full
|
|
control over dispatching.
|
|
However, this approach does not solve the dilemma that it is
|
|
sometimes useful that <a href="#:"><code>:</code></a> is global
|
|
and sometimes undesired.
|
|
The approach specified for this SRFI addresses this dilemma
|
|
by offering options.<P>
|
|
|
|
Another alternative for dealing with the dispatching
|
|
problem is adding an optional argument to the syntax of
|
|
<a href="#:"><code>:</code></a> through which the dispatcher
|
|
can be passed explicitly.
|
|
However, as <a href="#:"><code>:</code></a> has variable
|
|
arity and the identifier for the variable cannot be
|
|
distinguished from any value for a dispatcher,
|
|
this is syntactically problematic.
|
|
|
|
<H3>Why is there no local mechanism for adding to <a href="#:"><code>:</code></a>?</H3>
|
|
|
|
According to <a href="#R5RS">[R5RS, 7.1.6.]</a> macros can only
|
|
be defined at the level of the <code><program></code> syntax.
|
|
This implies that the scope of typed generators cannot easily be
|
|
limited to local scopes.
|
|
As typed and dispatched generators go together,
|
|
there is also no strong need for a limited scope
|
|
of dispatched generators either.
|
|
Furthermore, locally extendable dispatchers are another major
|
|
headache to those trying to understand other people's code.
|
|
|
|
<H3>Why are dispatchers unary?</H3>
|
|
|
|
As defined in <a href="#:dispatched"><code>:dispatched</code></a>,
|
|
a dispatching procedure is called with a single argument being
|
|
the list of values to dispatch on.
|
|
An alternative is to <code>apply</code> the dispatcher to the
|
|
list of values to dispatch on, which would be more natural in Scheme.<P>
|
|
|
|
The reason for not using <code>apply</code> is a minor
|
|
improvement in efficiency.
|
|
Every time <code>apply</code> is used on a procedure of variable
|
|
arity, an object containing the argument list is allocated on
|
|
the heap.
|
|
As a dispatcher may call many other dispatchers, this will adds
|
|
to the overhead of dispatching, which is relevant in inner loops.
|
|
|
|
<H3>Why are there two fold comprehensions?</H3>
|
|
|
|
The reason for having two fold comprehensions
|
|
(<a href="#fold-ec"><code>fold-ec</code></a> and
|
|
<a href="#fold3-ec"><code>fold3-ec</code></a>) is efficiency.<P>
|
|
|
|
Clearly, the more general construction is
|
|
<a href="#fold3-ec"><code>fold3-ec</code></a>
|
|
as it allows individual treatment of the empty
|
|
sequence case and the singleton sequence case.
|
|
However, this comes at the price of more book-keeping
|
|
as can be seen from the
|
|
<a href="#fold3-ec-example">implementation example</a>.
|
|
As the overhead is located within inner loops,
|
|
it makes sense to define another fold comprehension
|
|
for the case where the added flexibility is not needed.
|
|
This is <a href="#fold-ec"><code>fold-ec</code></a>.<P>
|
|
|
|
The names <code>fold-ec</code> and <code>fold3-ec</code>
|
|
have been chosen for the comprehensions in order to stay
|
|
clear any other 'fold' that may be around.
|
|
|
|
<H3>Why is <code>:char-range</code> not defined by <code>integer->char</code>?</H3>
|
|
|
|
The definition of <a href="#:char-range"><code>:char-range</code></a>
|
|
specifies a sequence of adjacent characters ordered by <code>char<=?</code>.
|
|
The reason for not using <code>char->integer</code> and
|
|
<code>integer->char</code> is the fact that
|
|
<a href="#R5RS">[R5RS, 6.3.4.]</a> leaves it to the implementation
|
|
whether the integers representing characters are consecutive or not.
|
|
In effect, this underspecification is inherited by <code>:char-range</code>.
|
|
|
|
|
|
<H1><a name="related-work"></a>Related Work and Acknowledgements</H1>
|
|
|
|
Several other proposals related to the mechanism specified here exists.
|
|
The following mechanisms are made for and in Scheme (or at least a
|
|
specific dialect thereof):<P>
|
|
|
|
First of all, the report <a href="#R5RS">[R5RS]</a> of Scheme itself
|
|
defines two constructs for writing loops: <code>do</code> and
|
|
named-<code>let</code>.
|
|
Both constructs express a single loop (not nested),
|
|
possibly with several variables running in parallel,
|
|
based on explicit manipulation of the state variables.
|
|
For example <code>(do ((x 0 (+ x 1))) ((= x 10)) (display x))</code>
|
|
explicitly mentions how to obtain the next binding of <code>x</code>.<P>
|
|
|
|
Richard Kelsey's "Macros for writing loops", <a href="#MWL">[MWL]</a>
|
|
are an extension to Scheme48 to simplify the formulation of loops.
|
|
The basic idea is to stick with a <code>do</code>-like syntax for
|
|
more sophisticated loop constructs, not necessarily manipulating
|
|
a state variable explicitly.
|
|
For example, <code>(list* x '(1 2 3))</code> expresses an enumeration
|
|
of the variable <code>x</code> through the list <code>(1 2 3)</code>
|
|
without explicit state manipulation.
|
|
The iteration constructs of <a href="#MWL">[MWL]</a>, named
|
|
<code>iterate</code> and <code>reduce</code>,
|
|
express a single (not nested) loop (<code>iterate</code>) or
|
|
comprehension (<code>reduce</code>) with any number of
|
|
parallel enumerations.
|
|
A most important feature of the <a href="#MWL">[MWL]</a>-concept
|
|
is a modular way to add sequence types (generators).
|
|
In effect, the addition of a new sequence type does not
|
|
require a modification of the existing macros.
|
|
This is achieved by carefully limiting the expressive
|
|
power of the loop constructs and by using the macros
|
|
in <I>Continuation Passing Style</I> to call other macros.
|
|
The <a href="#MWL">[MWL]</a>-concept, and its implementation,
|
|
were most influential for this SRFI.<P>
|
|
|
|
Another related mechanism is the library of streams recently
|
|
submitted by Phil L. Bewig as <a href="#SRFI40">[SRFI 40]</a>.
|
|
The library contains a data type to represent even
|
|
streams (both car and cdr potentially delayed) and
|
|
defines procedures for manipulating these streams.
|
|
Moreover, the macro <code>stream-of</code> defines a
|
|
lazy comprehension resulting in the stream of values of
|
|
an expression subject to generators and filters.
|
|
A fixed set of generators (lists, vector, string, port,
|
|
and naturally: streams) is supported; extending the
|
|
list of generators requires changing <code>stream-of</code>.
|
|
Nevertheless, modularity is high since it is easy to define
|
|
a procedure producing a stream object and this can be
|
|
used for enumeration.
|
|
The order of enumeration is left unspecified to allow
|
|
interleaving of generators (also refer to
|
|
<a href="#dovetail">above</a>.)
|
|
Before Phil submitted his SRFIs, we had a short
|
|
discussion in which we clarified the semantic and syntactic
|
|
differences of our approaches.
|
|
It turned out that the mechanisms are sufficiently
|
|
different not to unify them.
|
|
The most important difference is the design rationale:
|
|
Phil created his library to support the stream-paradigm
|
|
in Scheme, inspired by the work done for Haskell and
|
|
other lazy languages, and intrigued by the beauty
|
|
of programming with infinite streams.
|
|
My work only aims at a convenient way of expressing
|
|
frequent patterns of loops in a compact way.
|
|
For what it is worth, section <a href="#srfi40-ec">SRFI40-ec</a>
|
|
contains a suggestion for extending the eager comprehension
|
|
mechanism for SRFI40-streams.<P>
|
|
|
|
Phil's work on streams and lazy comprehensions in Scheme
|
|
triggered Eli Barzilay to implement a library of eager
|
|
comprehensions for PLT-Scheme, <a href="#Eli">[Eli]</a>.
|
|
The mechanism implemented by Eli is in essence very
|
|
similar to the one proposed in this SRFI, and the two
|
|
efforts have been independent until recently.
|
|
Syntactically, Eli uses infix operators for generators,
|
|
whereas this SRFI is purely prefix, and Eli uses the
|
|
[<I>expr</I> | <I>outer</I> .. <I>inner</I>] convention
|
|
for nesting, whereas this SRFI uses the
|
|
[<I>outer</I> .. <I>inner</I> | <I>expr</I>]
|
|
<a href="#convention">convention</a>.
|
|
Semantically, Eli's mechanism defines more flexible
|
|
loops than this SRFI.
|
|
Comprehensions are regarded as generalized collection
|
|
processes like fold and reduce.
|
|
The mechanism in this SRFI is more restricted with respect
|
|
to control flow (there is no general <code>while</code>)
|
|
and more extensive with respect to generators and
|
|
comprehensions.
|
|
Despite the strong conceptual similarity, the design
|
|
rationales are different.
|
|
This SRFI focuses on portability and modular extension,
|
|
whatever that may cost in terms of expressive power.<P>
|
|
|
|
Finally, I would like to thank Mike Sperber for his
|
|
encouragement to proceed with the SRFI and for several
|
|
discussions of the matter.
|
|
In particular, the dispatching mechanism evolved
|
|
rapidly during discussions with Mike.
|
|
|
|
|
|
<H1><a name="refimpl"></a>Implementation</H1>
|
|
|
|
The reference implementation focuses on portability,
|
|
performance, readability and simplicity, roughly in this order.
|
|
It is written in <a href="#R5RS">[R5RS]</a>-Scheme
|
|
(including macros) extended by <a href="#SRFI23">[SRFI 23]</a>
|
|
(<code>error</code>).
|
|
The reference implementation was developed
|
|
under <a href="#Scheme48">Scheme48</a> (0.57),
|
|
<a href="#PLT">PLT</a> (202, 204), and
|
|
<a href="#SCM">SCM</a> (5d7).<P>
|
|
|
|
The file <a href="http://srfi.schemers.org/srfi-42/ec.scm">ec.scm</a> is the source of
|
|
the reference implementation.
|
|
It also contains comments concerning potential problems.
|
|
Implementors might find the file <a href="http://srfi.schemers.org/srfi-42/design.scm">design.scm</a>
|
|
helpful.
|
|
It contains alternative implementations of certain comprehensions
|
|
and generators in order to simplify tuning the implementation
|
|
of this SRFI for different Scheme systems.<P>
|
|
|
|
The file <a href="http://srfi.schemers.org/srfi-42/examples.scm">examples.scm</a> contains a
|
|
collection of examples, and some code checking their results.
|
|
The purpose of most examples is detecting implementation errors,
|
|
but the section 'Less artificial examples' contains a few
|
|
real-world examples. <P>
|
|
|
|
The file <a href="http://srfi.schemers.org/srfi-42/timing.scm">timing.scm</a> contains some
|
|
code to measure an idea of performance of the comprehensions.
|
|
A hand-coded <code>do</code>-loop, the typed generator
|
|
<code>(<a href="#:range">:range</a> </code><I>n</I><code>)</code>,
|
|
and the dispatching generator
|
|
<code>(<a href="#:">:</a> </code><I>n</I><code>)</code>
|
|
are compared.
|
|
For each loop we compare the time needed
|
|
per iteration and the time needed to construct the loop (the startup delay).
|
|
As a rule of thumb, <code>:range</code> is as fast (or slightly faster)
|
|
as a hand-coded <code>do</code>-loop per iteration and needs about a
|
|
third more time for startup (due to type checking of the argument).
|
|
The dispatching generator needs about twice the time per iteration
|
|
(due to calling the generator procedure) and needs about five times
|
|
as long for startup (due to dispatching).<P>
|
|
|
|
The file <a href="http://srfi.schemers.org/srfi-42/extension.scm">extension.scm</a> contains
|
|
examples for adding new generators and comprehensions.<P>
|
|
|
|
<H1>References</H1>
|
|
|
|
<TABLE>
|
|
|
|
<TR>
|
|
<TD><a name="R5RS">[R5RS]</a>
|
|
<TD>Richard Kelsey, William Clinger, and Jonathan Rees (eds.):
|
|
Revised(5) Report on the Algorithmic Language Scheme of
|
|
20 February 1998.
|
|
Higher-Order and Symbolic Computation, Vol. 11, No. 1, September 1998.
|
|
<a href="http://schemers.org/Documents/Standards/R5RS/">
|
|
http://schemers.org/Documents/Standards/R5RS/</a>.
|
|
</TR>
|
|
|
|
<TR>
|
|
<TD><a name="MWL">[MWL]</a>
|
|
<TD>Richard Kelsey, Jonathan Rees:
|
|
The Incomplete Scheme48 Reference Manual for Release 0.57 (July 15, 2001).
|
|
Section "Macros for writing loops".
|
|
<a href="http://s48.org/0.57/manual/s48manual_49.html">http://s48.org/0.57/manual/s48manual_49.html</a>
|
|
</TR>
|
|
|
|
|
|
<TR>
|
|
<TD><a name="SRFI1">[SRFI 1]</a>
|
|
<TD>Olin Shivers: List Library.
|
|
<a href="http://srfi.schemers.org/srfi-1/">http://srfi.schemers.org/srfi-1/</a>
|
|
</TR>
|
|
|
|
<TR>
|
|
<TD><a name="SRFI23">[SRFI 23]</a>
|
|
<TD>Stephan Houben: Error reporting mechanism
|
|
<a href="http://srfi.schemers.org/srfi-23/">http://srfi.schemers.org/srfi-23/</a>
|
|
</TR>
|
|
|
|
|
|
<TR>
|
|
<TD><a name="SRFI25">[SRFI 25]</a>
|
|
<TD>Jussi Piitulainen: Multi-dimensional Array Primitives.
|
|
<a href="http://srfi.schemers.org/srfi-25/">http://srfi.schemers.org/srfi-25/</a>
|
|
</TR>
|
|
|
|
<TR>
|
|
<TD><a name="SRFI27">[SRFI 27]</a>
|
|
<TD>Sebastian Egner: Sources of Random Bits.
|
|
<a href="http://srfi.schemers.org/srfi-27/">http://srfi.schemers.org/srfi-27/</a>
|
|
</TR>
|
|
|
|
<TR>
|
|
<TD><a name="SRFI33">[SRFI 33]</a>
|
|
<TD>Olin Shivers: Integer Bitwise-operation Library.
|
|
<a href="http://srfi.schemers.org/srfi-33/">http://srfi.schemers.org/srfi-33/</a>
|
|
</TR>
|
|
|
|
<TR>
|
|
<TD><a name="SRFI39">[SRFI 39]</a>
|
|
<TD>Marc Feeley: Parameter objects.
|
|
<a href="http://srfi.schemers.org/srfi-39/">http://srfi.schemers.org/srfi-39/</a>
|
|
</TR>
|
|
|
|
<TR>
|
|
<TD><a name="SRFI40">[SRFI 40]</a>
|
|
<TD>Philip L. Bewig: A Library of Streams.
|
|
<a href="http://srfi.schemers.org/srfi-40/">http://srfi.schemers.org/srfi-40/</a>
|
|
</TR>
|
|
|
|
<TR>
|
|
<TD><a name="Eli">[Eli]</a>
|
|
<TD>Eli Barzilay: Documentation for "misc.ss". 2002.
|
|
<a href="http://www.cs.cornell.edu/eli/Swindle/misc-doc.html#collect">http://www.cs.cornell.edu/eli/Swindle/misc-doc.html#collect</a>
|
|
</TR>
|
|
|
|
<TR>
|
|
<TD><a name=Folds>[Folds]</a>
|
|
<TD>John David Stone: Folds and reductions.
|
|
Posting in relation to <a href="#SRFI1">[SRFI 1]</a> on 8-Jan-1999.
|
|
<a href="http://srfi.schemers.org/srfi-1/mail-archive/msg00021.html">http://srfi.schemers.org/srfi-1/mail-archive/msg00021.html</a>
|
|
</TR>
|
|
|
|
<TR>
|
|
<TD><a name="Haskell">[Haskell]</a>
|
|
<TD>Simon L. Peyton Jones, John Hughes: The Haskell 98 Report 1 February 1999.
|
|
Section 3.11 "List Comprehensions".
|
|
<a href="http://www.haskell.org/onlinereport/exps.html#sect3.11">http://www.haskell.org/onlinereport/exps.html#sect3.11</a>
|
|
</TR>
|
|
|
|
<TR>
|
|
<TD><a name="Python">[Python]</a>
|
|
<TD>Guido van Rossum, Fred L. Drake Jr. (eds.):
|
|
Python Reference Manual.
|
|
Section 5.2.4 "List displays".
|
|
Release 2.2, December 21, 2001.
|
|
<a href="http://python.org/doc/2.2/ref/lists.html">http://python.org/doc/2.2/ref/lists.html</a>
|
|
</TR>
|
|
|
|
<TR>
|
|
<TD><a name="SICP">[SICP]</a>
|
|
<TD>Harold Abelson, Gerald J. Sussman, Julie Sussman:
|
|
Structure and Interpretation of Computer Programs.
|
|
MIT Press, 1985.
|
|
<a href="http://mitpress.mit.edu/sicp/">http://mitpress.mit.edu/sicp/</a>
|
|
</TR>
|
|
|
|
<TR>
|
|
<TD><a name="IFPL">[IFPL]</a>
|
|
<TD>Philip Wadler: List Comprehensions (Chapter 7). In:
|
|
Simon L. Peyton Jones: The Implementation of Functional Programming Languages.
|
|
Prentice Hall, 1987.
|
|
</TR>
|
|
|
|
<TR>
|
|
<TD><a name="Scheme48">[Scheme48]</a>
|
|
<TD>Richard Kelsey, Jonathan Rees: Scheme48 Release 0.57 (July 15, 2001).
|
|
<a href="http://s48.org/">http://s48.org/</a>
|
|
</TR>
|
|
|
|
<TR>
|
|
<TD><a name="SCM">[SCM]</a>
|
|
<TD>Aubrey Jaffer: SCM Scheme Implementation. Version 5d7 (November 27, 2002).
|
|
<a href="http://www.swiss.ai.mit.edu/~jaffer/SCM.html">http://www.swiss.ai.mit.edu/~jaffer/SCM.html</a>
|
|
</TR>
|
|
|
|
<TR>
|
|
<TD><a name="PLT">[PLT]</a>
|
|
<TD>PLT People: PLT Scheme, DrScheme Version 203.
|
|
<a href="http://www.plt-scheme.org/">http://www.plt-scheme.org/</a>
|
|
</TR>
|
|
|
|
<TR>
|
|
<TD><a name="SCSH">[Scsh]</a>
|
|
<TD>Olin Shivers, Brian D. Carlstrom, Martin Gasbichler, Mike Sperber:
|
|
Scsh Reference Manual.
|
|
For scsh release 0.6.3.
|
|
<a href="http://scsh.net/">http://scsh.net/</a>
|
|
</TR>
|
|
|
|
|
|
</TABLE>
|
|
|
|
|
|
|
|
<H1>Copyright</H1>
|
|
<p>Copyright (C) Sebastian Egner (2003). All Rights Reserved.</p>
|
|
|
|
<p>
|
|
Permission is hereby granted, free of charge, to any person obtaining
|
|
a copy of this software and associated documentation files (the
|
|
"Software"), to deal in the Software without restriction, including
|
|
without limitation the rights to use, copy, modify, merge, publish,
|
|
distribute, sublicense, and/or sell copies of the Software, and to
|
|
permit persons to whom the Software is furnished to do so, subject to
|
|
the following conditions:
|
|
</p>
|
|
<p>
|
|
The above copyright notice and this permission notice shall be
|
|
included in all copies or substantial portions of the Software.
|
|
</p>
|
|
<p>
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
</p>
|
|
|
|
|
|
<hr>
|
|
<address>Author: <a href="mailto:sebastian.egner@philips.com">Sebastian Egner</a></address>
|
|
<address>Editor: <a href="mailto:srfi-editors@srfi.schemers.org">Francisco Solsona</a></address>
|
|
<!-- Created: Tue Feb 4 13:21:00 MST 2003 -->
|
|
<!-- hhmts start -->
|
|
Last modified: Tue Apr 5 10:43:00 CEST 2005
|
|
<!-- hhmts end -->
|
|
|
|
</body>
|
|
</html>
|