Skip to content

Commit

Permalink
Add draft for eep 76: comprehension assignment
Browse files Browse the repository at this point in the history
  • Loading branch information
richcarl committed Jan 10, 2025
1 parent 6bc544a commit 8c54377
Showing 1 changed file with 118 additions and 0 deletions.
118 changes: 118 additions & 0 deletions eeps/eep-0076.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
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 76: 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.)

The only 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 8c54377

Please sign in to comment.