Skip to content

Commit

Permalink
Add draft for eep 77: comprehension assignment
Browse files Browse the repository at this point in the history
  • Loading branch information
richcarl committed Jan 11, 2025
1 parent 6bc544a commit 0a3486d
Showing 1 changed file with 121 additions and 0 deletions.
121 changes: 121 additions & 0 deletions eeps/eep-0077.md
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: "

0 comments on commit 0a3486d

Please sign in to comment.