Make assignment (match) in comprehension work as strict generator
Implements EEP 77 (https://github.com/erlang/eep/pull/74)
It would be useful to be able to easily bind variables in the qualifiers of a comprehension, for example:
[Z || X <- Ls,
{foo, Y} = g(X),
Z <- f(Y, h(Y))].
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; and someone might edit the code later and accidentally add extra elements to the right hand side, causing unintended Cartesian combinations.
It is in fact already allowed syntactically to have a = match expression in the qualifiers, but this is just interpreted as any other expression - thus expected to produce a boolean value, and if false, the current element will be skipped. Hence, any qualifier to the right of a V = Expr match will only execute if V is true. We can therefore expect that no such uses exist in practice. (The OTP code base has been checked and does not contain any.) To be completely safe, matches in a comprehension qualifier position can first be made an error, and then allowed with these binding semantics in the following major release.
This is related but orthogonal to #9134. It would make those few cases where variables have been exported from subexpressions of qualifiers much nicer to rewrite than requiring them to be singleton generators.
CT Test Results
7 files 573 suites 2h 0m 38s ⏱️ 4 475 tests 4 347 ✅ 128 💤 0 ❌ 10 668 runs 10 512 ✅ 156 💤 0 ❌
Results for commit 2480ad70.
:recycle: This comment has been updated with latest results.
To speed up review, make sure that you have read Contributing to Erlang/OTP and that all checks pass.
See the TESTING and DEVELOPMENT HowTo guides for details about how to run test locally.
Artifacts
// Erlang/OTP Github Action Bot
Correction: 1 occurence in the OTP test suites, in singleton_inference.erl, which is weird enough that I won't even try to rewrite it myself. Maybe @jhogberg who wrote it (commit ebc461e182) can help?
Turned into a feature. Becomes an error if it occurs when the feature is not enabled.
Also fixed multiple problems with the features implementation.
Rebased on #10376 to make the Features fix a separate thing.
Rebased again and updated feature introduced version to 29.
OTB accepted the EEP, but asks for some editorial updates of the EEP:
- Discussion of compatibility should be under its own heading in the EEP.
- Please add some more examples, showing more realistic use cases. The existing example is very complicated. It would be better to have a few simpler examples.
- Clarify whether using an assigned variable can be used on the left-hand side of the comprehension and give an example of what happens.
Regarding the implementation, I couldn't get it to work. All I got was:
t.erl:9:6: matches using '=' are not allowed in comprehension qualifiers
unless the experimental 'compr_assign' language feature is enabled.
With 'compr_assign' enabled, a match 'P = E' will behave as a
strict generator 'P <-:- [E]'."
% 9| {foo, Y} = id({foo, X}),
% | ^
I tried three ways to enable the feature.
I tried three ways to enable the feature.
Fixed. I also made some clarifications (comments and a renamed internal function) to how the state of features during parsing/compilation works.
Thanks, I've manually confirmed that the example can be compiled.
I can't see that you have added any new test cases to actually test assignments in comprehensions. I suggest that you enable the feature in the existing lc_SUITE and add some tests.
Does erl_eval happen to just work or does it need to be updated? In any case, there should be a test case in erl_eval_SUITE.