Add tuple-valued comprehensions to go with list and binary comprehensions.
Add tuple generators to go with list and binary generators.
Fix a syntax botch in comprehension qualifiers by explicitly recognising pattern = value bindings and treating them in a way that makes sense.
Currently, Erlang has
'[' Expr '||' Generators-And-Tests ']'
'<<' Expr '||' Generators-And-Tests '>>'
for generating lists and binaries, but there is no corresponding form for generating tuples. We add
'{' Expr '||' Generators-And-Tests '}'
with the meaning that { E || G }
has the same behaviour as
erlang:list_to_tuple([E || G])
except that it need not call
erlang:list_to_tuple/1
.
Currently, Erlang comprehensions allow
Pattern '<-' Expr
to enumerate over list and
Pattern '<=' Expr
to enumerate over binaries. The second of these forms is, I must say, not just asking for trouble, but screaming for it. This proposal adds three new forms:
Pattern '[' '<-' ']' Expr
Pattern '{' '<-' '}' Expr
Pattern '<<' '<-' '>>' Expr
for enumerating over lists, tuples, and binaries respectively,
providing an iconic representation of what Expr should be.
[<-]
and << <- >>
have exactly the same semantics as the
existing <-
and <=
do. The semantics of Pattern {<=}
Expr
is that of Pattern <- erlang:tuple_to_list(Expr)
, except that
erlang:tuple_to_list/1
need not be called.
Currently the Generators-And-Tests part allows a sequence of
generators and tests, where a test is any expression. A test
must evaluate to either ‘false’ or ‘true’. The form Pattern = Expr
is syntactically an expression, so is allowed as a test.
However, in context, this makes no sense. For a given Expr,
there are four possible outcomes:
This proposal changes part of the description of comprehensions to each Qualifier is either a generator, a binder, or a filter.
A generator is a list generator, a tuple generator, or a bit string generator.
A list generator is written as
Pattern <- List_Expr
or as
Pattern [<-] List_Expr
where List_Expr is an expression which must evaluate to a list of terms.
A tuple generator is written as
Pattern {<-} Tuple_Expr
where Tuple_Expr is an expression which must evaluate to a tuple of terms.
A bit string generator is written as
Bit_String_Pattern <= Bit_String_Expr
or as
Bit_String_Pattern << <- >> Bit_String_Expr
where Bit_String_Expr is an expression which must evaluate to a bit string.
The variables in the generator patterns shadow variables in the function clause surrounding the comprehension. These variables are not visible outside the comprehension.
A binder has the form
Pattern = Expr
or
Pattern = Binder
This evaluates the Expr and matches the result against the Pattern, binding variables in it. The variables in the binder patterns shadow variables in the function clause surrounding the comprehension. These variables are not visible outside the comprehension.
A filter is an expression which evaluates to ‘true’ or ‘false’. They are not limited to being guard tests.
Using Clean as well as Erlang, the lack of tuple comprehensions and tuple generators is an irritation. It is possible to get the desired effect in the existing language, but especially since bit string comprehensions were added to the language, the omission seems utterly pointless. The new forms are easier to think of and easier to read than forms that go through list comprehensions.
Haskell list comprehensions allow generators, filters, and ‘let’ bindings. The lack of let bindings in Erlang list comprehensions is difficult to understand; the fact that what LOOKS like Erlang’s equivalent of let bindings is allowed but misbehaves at run time is difficult to forgive.
The syntax for tuple comprehensions is obvious; no other syntax would be tolerable.
The syntax for tuple generators has a certain gawky charm; perhaps only a mother could love it. I tried <-[] <-{} <-«» but had trouble getting Yecc to like those. If it can be squeezed past Yecc’s limited lookahead somehow, the forms with the arrow outside the brackets would be prettier. Contrast
{ X+1 || X {<-} Xs }
{ X+1 || X <-{} Xs }
The way Erlang currently allows Pattern = Expr in comprehension qualifiers but gives it a completely useless meaning is a syntax bug that needs urgent correction. One approach is to recognise attempts to use the form and report them as syntax errors; to me it seems better to implement it so that it works as expected.
All three of these extensions can be implemented by mapping to the current language:
{ E || GT } => erlang:list_to_tuple([E || GT])
P {<-} E => P <- erlang:tuple_to_list(E)
P = E => P <- [E]
and the reference implementation does exactly that. However, better implementations are possible, as for that matter are better implementations of list comprehension, and in the mean time at least the code will be no less efficient than what the programmer could have written and the source will be more intention-revealing.
The new comprehension and generator forms are currently syntax errors, so no existing code can be affected by them.
The new binder form (or rather, the newly correct recognition of binder forms) is currently allowed by the Erlang compiler. However, as explained above, it cannot possibly be USEFUL with its current reading. It is conceivable that there might be test programs designed to elicit the bug which will stop working once the syntax bug is fixed, but it is not likely that any real code will be affected.
The auxiliary file eep-0012-1.diff
is a patch file to be
applied to erl_parse.yrl
. The patched file has been checked by yecc
,
which is happy with it. However, that’s all the testing that has been done.
This implementation does the three source to source rewrites described in the previous section, entirely in the parser. The rest of the Erlang system needs no changes whatever.
This document has been placed in the public domain.