Skip to content

Commit 0c9ad45

Browse files
Merge pull request #74 from richcarl/comprehension-assign
EEP-77: comprehension assignment
2 parents c0d4e56 + 0a3486d commit 0c9ad45

File tree

1 file changed

+121
-0
lines changed

1 file changed

+121
-0
lines changed

eeps/eep-0077.md

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
Author: Richard Carlsson <carlsson.richard(at)gmail(dot)com>
2+
Status: Draft
3+
Type: Standards Track
4+
Created: 10-Jan-2025
5+
Erlang-Version: OTP-28.0
6+
Post-History:
7+
****
8+
EEP 77: Assignment in Comprehensions
9+
----
10+
11+
Abstract
12+
========
13+
14+
This EEP adds assignments to comprehension qualifier lists, providing
15+
a convenient and readable alternative to other syntactical tricks.
16+
17+
Rationale
18+
=========
19+
20+
It would often be useful to be able to easily bind variables in the
21+
qualifier sequence of a comprehension, for example:
22+
23+
```erlang
24+
[Z || X <- Ls,
25+
{foo, Y} = g(X),
26+
Z <- f(Y, h(Y))].
27+
```
28+
29+
using plain `Pattern = ...,` entries between qualifiers.
30+
31+
You can do this today by writing a singleton generator:
32+
33+
```erlang
34+
[Z || X <- Ls,
35+
{foo, Y} <- [g(X)], % produce single element
36+
Z <- f(Y, h(Y))].
37+
```
38+
39+
but this has some drawbacks:
40+
41+
- The intent is not clear to the reader
42+
- You have to remember to write a single element list around the
43+
right hand side argument (which itself could be a list)
44+
- You probably want it to be a strict generator so typos don't just
45+
silently yield no elements
46+
- Someone editing the code later may accidentally add extra
47+
elements to the right hand side list, causing unintended Cartesian
48+
combinations
49+
50+
Another trick is to piggy-back on a boolean test, using export of
51+
bindings from within a subexpression to propagate to the subsequent
52+
qualifiers:
53+
54+
```erlang
55+
[Z || X <- Ls,
56+
is_tuple(Y = g(X)),
57+
Z <- f(Y, h(element(2,Y)))].
58+
```
59+
60+
which is very much not a recommended style in Erlang, making it hard
61+
to see where the bindings come from. It also relies on having a
62+
suitable test as part of the qualifiers for the value you want to
63+
bind. If you don't, it is possible to invent a dummy always-true test:
64+
65+
```erlang
66+
[Z || X <- Ls,
67+
foobar =/= (Y = g(X)),
68+
Z <- f(Y, h(Y))].
69+
```
70+
71+
Such tricks are very bad for readability and maintenance of the code,
72+
making the logic hard to follow.
73+
74+
Specification
75+
========================
76+
77+
It is in fact already allowed syntactically to have a `Pattern = ...`
78+
match expression in the qualifiers. This however gets interpreted as
79+
any other expression - thus expected to produce a boolean value - and
80+
if `false`, the current element will be skipped.
81+
82+
Hence, any qualifier following after a match `Var = Expr` will only be
83+
executed if `Var` has the value `true`. We can therefore expect that
84+
no such uses exist in practice, because `Var` would be fully
85+
redundant. (The OTP code base has been checked and does not contain
86+
any.) To avoid incompatibilities with any possible existing code that
87+
relies on the current behaviour, the new semantics can be implemented
88+
as an optional feature at first, and an error be reported for such
89+
assignments when the feature flag is not enabled.
90+
91+
The main change needed is then for the compiler to detect a match
92+
expression `Pattern = Expr` in the qualifier list, and treat it as a
93+
strict singleton generator `Pattern <-:- [Expr]`.
94+
95+
Reference Implementation
96+
------------------------
97+
98+
A [reference implementation][GitHub branch] exists in the
99+
`lc-match-operator` branch of the author's GitHub account, together with a
100+
[GitHub pull request][GitHub PR] to the Erlang/OTP repository.
101+
102+
[GitHub branch]: https://github.com/richcarl/otp/tree/lc-match-operator
103+
"Reference implementation branch on GitHub"
104+
105+
[GitHub PR]: https://github.com/erlang/otp/pull/9153
106+
"GitHub Pull Request"
107+
108+
Copyright
109+
=========
110+
111+
This document is placed in the public domain or under the CC0-1.0-Universal
112+
license, whichever is more permissive.
113+
114+
[EmacsVar]: <> "Local Variables:"
115+
[EmacsVar]: <> "mode: indented-text"
116+
[EmacsVar]: <> "indent-tabs-mode: nil"
117+
[EmacsVar]: <> "sentence-end-double-space: t"
118+
[EmacsVar]: <> "fill-column: 70"
119+
[EmacsVar]: <> "coding: utf-8"
120+
[EmacsVar]: <> "End:"
121+
[VimVar]: <> " vim: set fileencoding=utf-8 expandtab shiftwidth=4 softtabstop=4: "

0 commit comments

Comments
 (0)