6

I am writing a simple example in Elixir and although it works I don't really understand how.

defmodule MyList do
  def sum([],acc \\ 0), do: acc
  def sum([head | tail], acc), do: sum(tail,acc + head)
end

When I call MyList.sum I get the expected result

sum([]) => 0
sum([1,2,3]) => 6

I cannot add a default param in the second sum because the compiler throws an error

def sum/2 has default values and multiple clauses, use a separate clause for declaring defaults

So my question is, how come sum([1,2,3]) works? It does not match any of the definitions. Is the function still tail recursive?

2
  • I'd like to know if this is tail recursive or not myself. I would think it could be since you're not hanging on to anything that should force the code to create a stack frame. Commented Mar 4, 2014 at 14:11
  • It is tail recursive. I've updated the response. Commented Mar 5, 2014 at 9:16

1 Answer 1

9

When you have a multiclause with optional arguments, you can specify defaults as a body-less clause:

defmodule MyList do
  def sum(list, acc \\ 0) # sets up default arguments

  def sum([],acc), do: acc
  def sum([head | tail], acc), do: sum(tail,acc + head)
end

Regarding your example, I'm just guessing, but I think that your code amounts to something like following:

defmodule MyList do
  # implicitly generated due to default argument
  def sum(list), do: sum(list, 0)

  def sum([],acc), do: acc
  def sum([head | tail], acc), do: sum(tail,acc + head)
end

Which is why sum([1,2,3]) works as well.

Edit: The function is definitely tail recursive. If the last thing a function does is a call of another function (or itself), then it is a tail call. So in this case, when we call sum(tail, acc + head), first the arguments are calculated, and then a tail recursive call happens.

Sign up to request clarification or add additional context in comments.

2 Comments

So, regarding my question, is the default param inherited from the first definition to the rest? It looks like this is what is happening and it would explain why defining a bodyless clause works
Yes, the default param is inherited (or rather passed). The second snippet explains this. The code in that snippet is an expanded equivalent of your original code. Function sum/1 will call sum/2 sending 0 as the second argument. So each sum/2 clause will receive this default value. Body-less clause does the same thing,, but it seems more explicit and cleaner, since the default value is taken out of any clause and specified separately at the top.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.