I'm studying Elixir and when I use only
or except
operators when importing functions from a module I need to specify an arity number. Why?
e.g.
import :math, only: [sqrt: 1]
or
import :math, except: [sin: 1, cos: 1]
Across the Erlang ecosystem functions are identified by name + arity. In most other languages you can overload functions by name. In other words, in the Erlang world foo/1 (that is, foo(one_arg)) is a completely different function than foo/2 (as in, foo(one_arg, two_arg)), but in Python or Ruby "foo" is the complete function identity and it can be invoked with a flexible number of arguments.
The convention is to name functions that mean the same thing the same name, especially in the case of recursively defined iterative functions like:
factorial(N) -> factorial(1, N).
factorial(A, 0) -> A;
factorial(A, N) -> factorial(A * N, N - 1).
Notice there are two periods, meaning there are two completely independent definitions here. We could just as well write:
fac(N) -> sum(1, N).
sum(A, 0) -> A;
sum(A, N) -> sum(A * N, N - 1).
But you will notice that the second version's savings in terms of character strokes is drastically outweighed by the convolution of its semantics -- the second version's internal function name is an outright lie!
The convention is to name related functions the same thing, but in actuality overloading functions by arity is not allowed in the Erlang ecosystem. To make such overloading acceptable would require significant feature additions to the compiler of a language that compiles to Erlang's bytecode, and that would be a pointless waste of painful effort. The current situation is about as good as we can get in a dynamically typed functional language (without it becoming a statically typed functional language... and that's another discussion entirely).
The end result is that you have to specify exactly what function you want to import, whether in Erlang or Elixir, and that means identifying it by name + arity. Recognizing that the common convention is to use the same name for functions that do the same thing but have different argument counts (often simply writing a cascade of curried definitions to enclose common defaults), Elixir provides a shortcut to including functions by groups instead of enumerating them.
So when you import :math, only: [sqrt: 1]
you only take math:sqrt/1
and left the rest of the module out (were there a math:sqrt/2
you would have ignored it). When you import :math, except: [sin: 1, cos: 1]
you take everything but math:sin/1
and math:cos/1
(were there a math:sin/2
you would have taken it). The name + arity is a distinct identity. Imagine a big KV store of available functions. The keys are {module, func, arity}
, meaning they are an atomic value to the system. If you're familiar with Erlang even a little this may strike you as familiar, because you deal with the tuple {Module, Function, Args}
all the time.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With