What’s going on here?

To do things in a different way than the original article, let’s check the docs first. According to the official Erlang/OTP documentation:

List comprehensions are written with the following syntax:

[Expr || Qualifier1,...,QualifierN]

Here, Expr is an arbitrary expression, and each Qualifier is either a generator or a filter.

A generator is written as:

Pattern <- ListExpr.

ListExpr must be an expression, which evaluates to a list of terms.

is written as: Pattern <- ListExpr. ListExpr must be an expression, which evaluates to a list of terms. A bit string generator is written as:

BitstringPattern <= BitStringExpr.

BitStringExpr must be an expression, which evaluates to a bitstring.

is written as: BitstringPattern <= BitStringExpr. BitStringExpr must be an expression, which evaluates to a bitstring. A filter is an expression, which evaluates to true or false.

With that in mind, it’s easy to see that our original list comprehension ([X || Acc]) is parsed as [Expr || Qualifier1], where Expr is X and Qualifier1 is Acc. And since Acc doesn’t look like Pattern <- ListExpr nor BitstringPattern <= BitStringExpr we can tell that the compiler treats it as a filter.

As you can see in the paragraphs above, Erlang impose no restrictions on how many filters or generators you must have. It just states that you must have at least one qualifier.

But the question remains: how should a list comprehension be evaluated when it has no generators?

Let’s see what the Erlang docs say about the semantics of list comprehensions:

A list comprehension returns a list, where the elements are the result of evaluating Expr for each combination of generator list elements and bit string generator elements, for which all filters are true.

In general, this statement is fine, but what happens when we do not have any generators? In that case, the meaning of “each combination of generator list elements and bit string generator elements” seems to be a little unclear.

Personally, I would’ve seen an empty list ([]) as the proper evaluation of any list comprehension without generators. But the implementors of this particular language feature had a different idea in mind. In Erlang/OTP (at least up to version 19.0) when the list comprehension has no generators the semantics are understood as follows:

A list comprehension returns a list, where the elements are the result of evaluating Expr when all filters are true.

Actually, I would’ve just opted for a much stricter syntax and required at least one generator for a list comprehension expression to be even syntactically correct. But that’s just me ;)