7

is there any way to define overload functions with different arity, e.g in C# I can just do:

foo(bar)

or

foo(bar, baz)

In Elixir, the only way to do that would be to put them in separate modules, which will get messy pretty quickly. Is there any way around it?

Edit: I had made a wrong assumption. The examples of overloaded functions I saw happened to have the same arity, so I (wrongly) assumed that this was a requirement. Functions are uniquely identified by their name and arity, so you can in fact overload functions with different arity.

Makyen
  • 31,849
  • 12
  • 86
  • 121
tldr
  • 11,924
  • 15
  • 75
  • 120

2 Answers2

13

In Erlang and Elixir, and unlike many other languages (such as C#), functions are uniquely identified by their name and arity, so technically foo(bar) and foo(bar, baz) are totally different functions. But that's really just a technicality, to write an 'overloaded' function in Elixir, you would write something like the following definition of sum:

defmodule Math do
  def sum(list),       do: sum(list, 0)
  def sum([], acc),    do: acc
  def sum([h|t], acc), do: sum(t, acc + h)
end
enedil
  • 1,605
  • 4
  • 18
  • 34
bitwalker
  • 9,061
  • 1
  • 35
  • 27
  • in the first line, how can the function's arity be 1? – tldr May 12 '14 at 05:16
  • 3
    @tldr: as bitwalker has said, sum/1 and sum/2 are different functions. Functions are identified by name AND arity. – Miroslav Prymek May 12 '14 at 07:27
  • oh, so that was a misunderstanding on my part - all the examples of overloaded functions had the same arity, so I assumed that was a requirement. – tldr May 12 '14 at 18:17
  • 2
    If you see many functions with the same name and arity, it's because some of those functions are pattern matching on their arguments, like in the example above with `sum([], acc)` and `sum([h|t], acc)`. In that case, the functions are tried in source order, so when you call `sum([1, 2, 3], 0)`, it will first try and match against `sum([], acc)` which will fail since `[1, 2, 3]` isn't an empty list, and then will try `sum([h|t], acc)` which will succeed, since the list has a head and a tail. You can't have two functions of the same name and arity that don't match on their arguments. – bitwalker May 12 '14 at 18:45
  • To clarify my last statement above, the following is not valid: `sum(list, acc), do: acc + 1` `sum(list, acc), do: acc + 2` But the following is: `sum([], acc), do: acc` `sum(list, acc), do: [h|t] = list; sum(t, acc + h)` The reason for that is because the first case cannot differentiate between the two function clauses, which isn't valid. The second case however can. – bitwalker May 12 '14 at 18:47
  • I see. Would the first case become valid if I added a type spec to each of the clauses, such that 'list' in the first clause is a string, and in the second clause it's a number? – tldr May 12 '14 at 20:09
  • Unfortunately no, since the compiler doesn't enforce the type specs, and it still needs to know how to resolve the which function to call at runtime. Think of type specs as metadata for static analysis, rather than the equivalent of type information as seen in your average statically typed language. – bitwalker May 13 '14 at 03:50
2

On this page see especially section 8.3 and following. Specifically this:

Function declarations also support guards and multiple clauses. If a function has several clauses, Elixir will try each clause until it finds one that matches. Here is an implementation of a function that checks if the given number is zero or not:

defmodule Math do
  def zero?(0) do
    true
  end

  def zero?(x) when is_number(x) do
    false
  end
end

Math.zero?(0)  #=> true
Math.zero?(1)  #=> false

Math.zero?([1,2,3])
#=> ** (FunctionClauseError)

Same function name with multiple overloads (although the concept is called clauses in the documentation) in a single module.

Onorio Catenacci
  • 14,928
  • 14
  • 81
  • 132
  • 1
    this just shows functions with the same arity. As I mentioned in my question, I'm looking for a way to overload functions with different arity. – tldr May 12 '14 at 18:14
  • 1
    Your comment in your question "In Elixir, the only way to do that would be to put them in separate modules," made me think you didn't understand the idea of multi-clause functions in a single module. Plus I added this answer for others who may see this question in the future. – Onorio Catenacci May 12 '14 at 18:21
  • My misunderstanding was that a function's clauses need to have the same arity (because the examples had clauses with the same arity). This is incorrect, and I have added it as an edit in my question. Since your example also has clauses with the same arity, it isn't relevant to the question. – tldr May 12 '14 at 20:04