Roger Johansson
Roger Johansson

Reputation: 23214

Elixir pipes invalid syntax

I'm having trouble understanding why this works:

1..1000 |> Stream.map(&(3 * &1)) |> Enum.sum

While this doesnt:

1..1000 |> Stream.map (&(3 * &1)) |> Enum.sum

The only difference is the space after .map To my understanding, Elixir should not care about white-space in this case.

Running the above code in iex yields the following error:

warning: you are piping into a function call without parentheses, which may be ambiguous. Please wrap the function you are piping into in parentheses. For example:

foo 1 |> bar 2 |> baz 3

Should be written as:
** (FunctionClauseError) no function clause matching in Enumerable.Function.reduce/3

foo(1) |> bar(2) |> baz(3)

(elixir) lib/enum.ex:2776: Enumerable.Function.reduce(#Function<6.54118792/1 in :erl_eval.expr/5>, {:cont, 0}, #Function<44.12486327/2 in Enum.reduce/3>)
(elixir) lib/enum.ex:1486: Enum.reduce/3

Why is the pipe operator making a distinction between the two cases here?

Upvotes: 3

Views: 516

Answers (1)

michalmuskala
michalmuskala

Reputation: 11288

The whitespace changes how the precedence is resolved:

iex(4)> quote(do: Stream.map(1) |> Enum.sum) |> Macro.to_string 
"Stream.map(1) |> Enum.sum()"

iex(5)> quote(do: Stream.map (1) |> Enum.sum) |> Macro.to_string
"Stream.map(1 |> Enum.sum())"

Furthermore Elixir does not support leaving space between function and parenthesis - it only works by accident for the unary functions, because parenthesis are optional: foo (1) is the same as foo((1)). You can see that it's not supported with functions with more arguments:

iex(2)> quote(do: foo (4, 5))
** (SyntaxError) iex:2: unexpected parentheses. 
If you are making a function call, do not insert spaces between
the function name and the opening parentheses. 
Syntax error before: '('

Upvotes: 11

Related Questions