Author:
Richard Carlsson <carlsson.richard(at)gmail(dot)com>
Status:
Draft
Type:
Standards Track
Created:
10-Jan-2025
Erlang-Version:
OTP-28.0

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:

[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:

[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:

[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:

[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 exists in the lc-match-operator branch of the author’s GitHub account, together with a GitHub pull request to the Erlang/OTP repository.

Copyright #

This document is placed in the public domain or under the CC0-1.0-Universal license, whichever is more permissive.