Can't use default values in multiple lines of recursive definition?

Hey Folks,

Just started learning Elixir, (going through Dave Thomas’ Programming Elixir book right now) and have a question on default values and why they’re not allowed in multiple lines of a recursive function definition.

For example: one of the exercises in the book, (p. 73) asks you to write a max(list) function to return the maximum value in a list. The code that I wrote to end up solving this is as follows:

def max([head | tail]), do: _max([head | tail], 0)
    # Private Functions for max
    defp _max([], maximum), do: maximum
    defp _max([head | tail], maximum) when head >= maximum, do: _max(tail, head)
    defp _max([_head | tail], maximum), do: _max(tail, maximum)

(note: there’s another line to deal with the max of a null set, but removed that to not cloud the issue)

which is fine, but that idea of writing that one line to define max as a public function solely to add a default value of 0 for the comparator, (i.e. “maximum”) and then do the rest via private function definitions seems like it should be unnecessary.

What I wanted to do was this:

def max([], maximum \\ 0), do: maximum
def max([head | tail], maximum \\ 0) when head >= maximum, do: max(tail, head)
def max([_head | tail], maximum \\ 0), do: max(tail, maximum)

with the idea being that a user could simply provide a list, (without “maximum”) but by having it there with the defaults, I could thus enable the two do clauses, as well, (without needing to resort to the whole public / private function thing in the first example).

Unfortunately, my linter kept complaining about putting default values on multiple lines of a recursive definition, and when I tried to compile it, (in case it was a glitch in the linter) I got the following:

** (CompileError) sum_without_accumulator.exs:19: definitions with multiple clauses and default values require a function head. Instead of:

def foo(:first_clause, b \\ :default) do ... end
def foo(:second_clause, b) do ... end

one should write:

def foo(a, b \\ :default)
def foo(:first_clause, b) do ... end
def foo(:second_clause, b) do ... end

def max/2 has multiple clauses and defines defaults in a clause with a body
sum_without_accumulator.exs:19: (module)
(stdlib) erl_eval.erl:670: :erl_eval.do_apply/6

which, I’m afraid, doesn’t seem to really be covering the issue I’m having, (or, at least, it’s not obvious to me why what it’s saying and what it’s taking issue with in my code are one and the same).

So, what I’m really wondering, (and hoping others can help me understand) is why can’t I use default values anywhere I want, so long as they should be, (as far as I can tell) syntactically correct, (i.e. the second definition)? Is it purely enforcing a stylistic, (hoping not) or is there something I’m missing?

Thanks in advance

A default value is on the function ‘head’, not a matcher. This means it is only on the first, or pull the head out into its own thing like you showed: :slight_smile:

def foo(a, b \\ :default)
def foo(:first_clause, b) do ... end
def foo(:second_clause, b) do ... end

That is what I do when I use default values. :slight_smile:

But think of it this way, the default value like the above is actually transformed into this by the compiler:

def foo(a), do: foo(a, :default)
def foo(:first_clause, b) do ... end
def foo(:second_clause, b) do ... end

Thus you can see that default values actually make new functions that just delegate to the main. :slight_smile:

1 Like