Elixir: function overloading with different arity
Asked Answered
C

2

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.

Crespo answered 12/5, 2014 at 3:14 Comment(0)
C
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
Caldron answered 12/5, 2014 at 3:54 Comment(7)
in the first line, how can the function's arity be 1?Crespo
@tldr: as bitwalker has said, sum/1 and sum/2 are different functions. Functions are identified by name AND arity.Horick
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.Crespo
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.Caldron
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.Caldron
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?Crespo
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.Caldron
S
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.

Stav answered 12/5, 2014 at 3:14 Comment(3)
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.Crespo
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.Stav
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.Crespo

© 2022 - 2024 — McMap. All rights reserved.