I'm new to Elixir and quite frankly, can't understand why function heads are needed when we have multiple clauses with default values. From the official docs:
If a function with default values has multiple clauses, it is required to create a function head (without an actual body) for declaring defaults:
defmodule Concat do def join(a, b \\ nil, sep \\ " ") def join(a, b, _sep) when is_nil(b) do a end def join(a, b, sep) do a <> sep <> b end end IO.puts Concat.join("Hello", "world") #=> Hello world IO.puts Concat.join("Hello", "world", "_") #=> Hello_world IO.puts Concat.join("Hello") #=> Hello
I read this and I'm like "Why?". What does this accomplish? It's not as if the two clauses are ambiguous and the compiler won't be able to deduce the right call. Besides, I just don't see how adding a function header helps.
It's likely I'm overlooking something very simple, so I'll be happy to have it explained to me.
Erlang functions (and therefore Elixir functions) are defined by two entities: name and arity. In your example, def join(a, b \\ nil, sep \\ " ")
is still of arity 3
and when the compiler finds the call like join("hello", "world")
, she knows that it should be routed to join/3
, passing "world"
as the second parameter and using a default value for the third one.
If we were allowed to specify default parameters in the function bodies itself, without having this function head, we could end up with:
def join(a, b \\ nil, sep), do: "default b"
def join(a, b, sep \\ nil), do: "default sep"
making a compiler to get stuck on call to join("hello", 42)
.
Please note, that you are requested for the head function if and only one or more of functions signatures has default parameter(s).
The goal of head function is to explicitly define the signature when called without default parameters values (join("hello")
.)
The only concrete benefit you get from this is that your default arguments will apply to all function calls. If the compiler let you do it the way you are suggesting, the equivalent would look like:
def join(a, b \\ nil, _sep \\ " ") when is_nil(b) do
a
end
def join(a, b \\ nil, _sep \\ " ") do
a <> sep <> b
end
The intent of it, from what I understand, is simply to reduce the confusion that can arise with defaults.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With