Reputation: 69
I created a simple function in Elixir that increments a value from a tuple by 1.
I named the module Test and named the function addToTuple. It takes one argument, which is the tuple itself.
def addToTuple({X,Y}) do
{X,Y+1}
end
I compiled the module via iex and got this warning:
warning: this expression will fail with ArithmeticError
Test.ex:68
Line 68 refers to {X,Y+1}. When I run the function with the command Test.addToTuple({4,5}), I keep getting this error:
** (FunctionClauseError) no function clause matching in Test.addToTuple/1
The following arguments were given to Test.addToTuple/1:
# 1
{4, 5}
Test.ex:67: Test.addToTuple/1
I was expecting to get {4,6}.
Any idea what's going on here?
UPDATE 1:
I changed X and Y to lower-case and it worked. However, this time I modified the function a bit to play around with atoms:
def addToTuple({A,x,y}) do
{A,x,y+1}
end
Then I called the function with this command: Test.addToTuple({:F,4,5}). I was expecting to get {:F,4,6}. But instead, I got this error:
** (FunctionClauseError) no function clause matching in Test.addToTuple/1
The following arguments were given to Test.addToTuple/1:
# 1
{:F, 4, 5}
Test.ex:67: Test.addToTuple/1
I thought using an upper-case character would be treated as an atom? How can this be resolved?
Upvotes: 0
Views: 381
Reputation: 48649
In your function definition here:
def addToTuple({A,x,y})
A
is not a variable, rather A
is an atom, which is like a constant. Normally, you write atoms like this:
:dog
:"my dog"
However, there is an alternative syntax for creating atoms: you can omit the leading colon and start the name with a capital letter, like this:
Dog
Elixir in Action(2nd) calls that an alias (for an atom). At compile time, Dog
is converted to the atom :"Elixir.Dog"
. Check it out:
iex(5)> Dog == :"Elixir.Dog"
true
Back to your function definition:
def addToTuple({A,x,y})
The only arguments that will match your function definition look like this:
{A, 1, 3}
{A, "hello", "world"}
{A, [1, 2, 3], [4, 5, 6]}
In other words, the only thing that will "match" A
in the function parameter list is A
. Elixir is not like other languages where only variables are allowed in the parameter list for a function definition. In other languages, a function definition looks like this:
def go(x, y, z) do
...
end
However, in elixir you can have constants in the parameter list for a function definition, like this:
def go(1, x, 2, y) do
...
end
In that function definition, the parameters include the constants 1
and 2
. If you call that function like this:
go(10, 20, 30, 40)
the function won't execute because there is no match of the function arguments 10, 20, 30, 40
to the function parameters 1, x, 2, y
. When you call a function, the function arguments are matched to the function parameters like this:
go(10, 20, 30, 40)
| | | |
V V V V
def go(1, x, 2, y) do
For that function call, elixir performs these matches/assignments:
1 = 10
x = 20
2 = 30
y = 40
Because 1 does not match 10, the function won't execute. The atom A
is just like the integers 1 and 2. The only thing that will match A
is A
.
Because elixir allows constants in a function's parameter list, that allows you to define a series of function clauses like this:
defmodule My do
def go(1, x) do
IO.puts x*2
end
def go(2, x) do
IO.puts x-4
end
def go(A, x) do
IO.puts x+5
end
end
My.go(2, 3)
My.go(A, 5)
Output:
-1
10
When you call a function that has several clauses defined, elixir starts with the first clause and tries to match the arguments in the function call to the parameter list. If there is no match, elixir tries the next function clause, and so on. When a match is found, that function clause executes. If no match is found, elixir throws an error. For instance, the call:
My.go(B, 3)
results in:
** (FunctionClauseError) no function clause matching in My.go/2
Another example:
defmodule My do
def calc({:add, x, y}) do
x + y
end
def calc({:subtract, x, y}) do
x - y
end
def calc({:multiply, x, y}) do
x * y
end
end
In iex:
~/elixir_programs$ iex my.exs
Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
Interactive Elixir (1.6.6) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> My.calc {:add, 10, 5}
15
iex(2)> My.calc {:subtract, 10, 5}
5
iex(3)> My.calc {:multiply, 10, 5}
50
Upvotes: 5