Disambiguate "arity denotation" and "arithmetic division" in Elixir

70 Views Asked by At

I learned that &(X.f/1) denotes the use of the capture operator (&) on a function f, which is defined inside the module X, and that f has arity 1.

I think I understood the semantic of this capturing, but it is the syntax which troubles me.

Looking at just X.f/1, this could also be understood as the result of the division of an niladic function X.f, by 1.

According to which rule is / understood here as a notation for arity instead of a division operator? Does the capture operator introduce a specific syntactic scope, which interprets the slash only as arity denotation?

(To clarify: With syntacic scope, I mean something similar in spirit as in shell programming (zsh, bash), where i.e. ((a+b)) is understood due the inclusion inside ((....)) as the addition of the shell variables a and b, without the need to write ...$a + $b...)

So, how does the compiler know, that / does not mean division here?

1

There are 1 best solutions below

3
sabiwara On BEST ANSWER

Does the capture operator introduce a specific syntactic scope, which interprets the slash only as arity denotation?

Yes. This actually happens in here in :elixir_expand where the capture operator is handled, then most of the work gets delegated to :elixir_fn.capture/4.

It is important to note that there is no distinction from a regular division at the AST level (you can confirm it in IEx with quote do: foo/1 and quote do: &foo/1). The compiler will simply interpret it differently when handling the capture.

Here is an example to illustrate the point: consider &:math.pi/&1. This is a valid function, equivalent to fn x -> :math.pi() / x end. Because the first clauses of :elixir_fn.capture/4 won't match (A is not an integer), it will consider / as a regular division expression, try to find an &1 arg (and succeed).

Finally, the error message from an invalid capture explains the logic well:

iex> &invalid
error: invalid args for &, expected one of:

 * &Mod.fun/arity to capture a remote function, such as &Enum.map/2
 * &fun/arity to capture a local or imported function, such as &is_atom/1
 * &some_code(&1, ...) containing at least one argument as &1, such as &List.flatten(&1)