Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the need for function heads in multiple clauses?

Tags:

elixir

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.

like image 311
ankush981 Avatar asked Dec 28 '16 06:12

ankush981


2 Answers

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").)

like image 97
Aleksei Matiushkin Avatar answered Nov 17 '22 18:11

Aleksei Matiushkin


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.

like image 27
dave Avatar answered Nov 17 '22 17:11

dave