Redefining functions?

Good morning,

First, I am a new learner and this is my first post ! I am really happy to join the community.

I am discovering Elixir syntax and I had a quick question:

Why can’t I use this syntax ?

Enum.all?([1, 30], is_number) 

If I do this I get the following error

** (CompileError) iex:9: undefined function is_number/0
(stdlib) lists.erl:1354: :lists.mapfoldl/3
(stdlib) lists.erl:1355: :lists.mapfoldl/3

I expected Elixir to find is_number/1

Instead, this works:

Enum.all?([1, 30], &is_number(&1))
Enum.all?([1, "hi"], fn x -> is_number(x) end)
1 Like

Functions in Elixir have both a name AND an arity (number of arguments it takes). For Elixir to find the function, it needs both. Therefore, if you use the & capture operator, you also need to specify the function’s arity:

Enum.all?([1, 30], &is_number/1)

With your previous syntax of Enum.all?([1, 30], is_number), Elixir doesn’t know the function’s arity. In this case, there is only one version of is_number, but other functions with the same name can have different arities, e.g. List.flatten/1 and List.flatten/2.

In Elixir, a function’s “identity” is composed of BOTH its name and arity. This is why you’ll see functions referred to as is_number/1 and not just is_number.

5 Likes

The question is more about why You need to use & than function arity.

Probably because

is_number(x)

has the same result as

fn x -> is_number(x) end

and You might wonder why wrapping this inside fn -> end.

But as soon as there is more than one params… You see it starts to be clearer why the 2 forms are differents.

There is another reason, fn allows to capture the present value of variables in the scope (Closure).

iex> a = 2
2
iex> myfunc = fn x -> a * x end
#Function<6.127694169/1 in :erl_eval.expr/5>
iex> a = 3
3
iex> Enum.map([1, 2], &myfunc.(&1))
[2, 4]

This last example is like having

fn x -> fn x -> a * x end end

It’s really fun to create functions from other functions so easily :slight_smile:

3 Likes