-
Notifications
You must be signed in to change notification settings - Fork 67
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add draft for eep 77: comprehension assignment
- Loading branch information
Showing
1 changed file
with
121 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
Author: Richard Carlsson <carlsson.richard(at)gmail(dot)com> | ||
Status: Draft | ||
Type: Standards Track | ||
Created: 10-Jan-2025 | ||
Erlang-Version: OTP-28.0 | ||
Post-History: | ||
**** | ||
EEP 77: Assignment in Comprehensions | ||
---- | ||
|
||
Abstract | ||
======== | ||
|
||
This EEP adds assignments to comprehension qualifier lists, providing | ||
a convenient and readable alternative to other syntactical tricks. | ||
|
||
Rationale | ||
========= | ||
|
||
It would often be useful to be able to easily bind variables in the | ||
qualifier sequence of a comprehension, for example: | ||
|
||
```erlang | ||
[Z || X <- Ls, | ||
{foo, Y} = g(X), | ||
Z <- f(Y, h(Y))]. | ||
``` | ||
|
||
using plain `Pattern = ...,` entries between qualifiers. | ||
|
||
You can do this today by writing a singleton generator: | ||
|
||
```erlang | ||
[Z || X <- Ls, | ||
{foo, Y} <- [g(X)], % produce single element | ||
Z <- f(Y, h(Y))]. | ||
``` | ||
|
||
but this has some drawbacks: | ||
|
||
- The intent is not clear to the reader | ||
- You have to remember to write a single element list around the | ||
right hand side argument (which itself could be a list) | ||
- You probably want it to be a strict generator so typos don't just | ||
silently yield no elements | ||
- Someone editing the code later may accidentally add extra | ||
elements to the right hand side list, causing unintended Cartesian | ||
combinations | ||
|
||
Another trick is to piggy-back on a boolean test, using export of | ||
bindings from within a subexpression to propagate to the subsequent | ||
qualifiers: | ||
|
||
```erlang | ||
[Z || X <- Ls, | ||
is_tuple(Y = g(X)), | ||
Z <- f(Y, h(element(2,Y)))]. | ||
``` | ||
|
||
which is very much not a recommended style in Erlang, making it hard | ||
to see where the bindings come from. It also relies on having a | ||
suitable test as part of the qualifiers for the value you want to | ||
bind. If you don't, it is possible to invent a dummy always-true test: | ||
|
||
```erlang | ||
[Z || X <- Ls, | ||
foobar =/= (Y = g(X)), | ||
Z <- f(Y, h(Y))]. | ||
``` | ||
|
||
Such tricks are very bad for readability and maintenance of the code, | ||
making the logic hard to follow. | ||
|
||
Specification | ||
======================== | ||
|
||
It is in fact already allowed syntactically to have a `Pattern = ...` | ||
match expression in the qualifiers. This however gets interpreted as | ||
any other expression - thus expected to produce a boolean value - and | ||
if `false`, the current element will be skipped. | ||
|
||
Hence, any qualifier following after a match `Var = Expr` will only be | ||
executed if `Var` has the value `true`. We can therefore expect that | ||
no such uses exist in practice, because `Var` would be fully | ||
redundant. (The OTP code base has been checked and does not contain | ||
any.) To avoid incompatibilities with any possible existing code that | ||
relies on the current behaviour, the new semantics can be implemented | ||
as an optional feature at first, and an error be reported for such | ||
assignments when the feature flag is not enabled. | ||
|
||
The main change needed is then for the compiler to detect a match | ||
expression `Pattern = Expr` in the qualifier list, and treat it as a | ||
strict singleton generator `Pattern <-:- [Expr]`. | ||
|
||
Reference Implementation | ||
------------------------ | ||
|
||
A [reference implementation][GitHub branch] exists in the | ||
`lc-match-operator` branch of the author's GitHub account, together with a | ||
[GitHub pull request][GitHub PR] to the Erlang/OTP repository. | ||
|
||
[GitHub branch]: https://github.com/richcarl/otp/tree/lc-match-operator | ||
"Reference implementation branch on GitHub" | ||
|
||
[GitHub PR]: https://github.com/erlang/otp/pull/9153 | ||
"GitHub Pull Request" | ||
|
||
Copyright | ||
========= | ||
|
||
This document is placed in the public domain or under the CC0-1.0-Universal | ||
license, whichever is more permissive. | ||
|
||
[EmacsVar]: <> "Local Variables:" | ||
[EmacsVar]: <> "mode: indented-text" | ||
[EmacsVar]: <> "indent-tabs-mode: nil" | ||
[EmacsVar]: <> "sentence-end-double-space: t" | ||
[EmacsVar]: <> "fill-column: 70" | ||
[EmacsVar]: <> "coding: utf-8" | ||
[EmacsVar]: <> "End:" | ||
[VimVar]: <> " vim: set fileencoding=utf-8 expandtab shiftwidth=4 softtabstop=4: " |