Why do I receive a FunctionClauseError ("no function clause matching") in this Elixir program, translated from Erlang?

jpsthecelt picture jpsthecelt · Jun 13, 2015 · Viewed 10.7k times · Source

**So, I've worked with Erlang, before, and am pretty comfortable with it. I am just trying to learn Elixir.

I was recently trying to translate a 'lazy caterers' example into elixir and am befuddled as to why it either doesn't compile, or compiles with a warning and doesn't work. What am I missing, here; Any ideas? The erlang code and 'run' are as follows:**

jps@GRASSKEET ~/dev/erlang
$ cat cater.erl

    -module(cater).
    -export([cater/1]).
    cater(0) -> 1;
    cater(N) when N>0 -> N + cater(N-1).

jps@GRASSKEET ~/dev/erlang
$ erl
Eshell V6.3  (abort with ^G)
1> c("cater.erl").
{ok,cater}
2> cater:cater(10).
56
3>*

When I write Cater.ex like so, it gets an error that doesn't make sense to me:

jps@GRASSKEET ~/dev/elix
$ cat Cater.ex

    defmodule Cater do
      def cut(0), do: 1
      def cut(N) when N>0, do: N + cut(N-1)
    end

jps@GRASSKEET ~/dev/elix
$ iex
Interactive Elixir (1.0.4) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> c("Cater.ex")
Cater.ex:1: warning: redefining module Cater
Cater.ex:3: warning: this expression will fail with ArithmeticError
[Cater]
iex(2)> Cater.cut(10)
** (FunctionClauseError) no function clause matching in Cater.cut/1
    Cater.ex:2: Cater.cut(10)
iex(2)>

Answer

Michelle Tilley picture Michelle Tilley · Jun 13, 2015

Just to expand on the problem and why you received the error you did:

Capitalized identifiers are treated as aliases for atoms. For example:

iex(1)> N == :Elixir.N
true

So when you have the following code:

iex(1)> defmodule Test do
...(1)> def foo, do: IO.puts "testing"
...(1)> end
iex(2)> Test.foo
testing

It's the same as saying

iex(3)> :Elixir.Test.foo
testing

Since uppercase identifiers are treated as symbols, you essentially wrote the following program:

defmodule Cater do
  def cut(0), do: 1
  def cut(:Elixir.N) when :Elixir.N>0, do: :Elixir.N + cut(:Elixir.N-1)
end

This is valid, because you can pattern match on atoms in the argument list, and :Elixir.N > 0 is a valid expression.

iex(1)> N > 0
true

Consider the following Elixir program and output:

iex(1)> defmodule Cater do
...(1)> def cut(0), do: IO.puts "cut(0)"
...(1)> def cut(N) when N > 0, do: IO.puts "cut(N)"
...(1)> end
iex(2)> Cater.cut(0)
cut(0)
iex(3)> Cater.cut(N)
cut(N)
iex(4)> Cater.cut(:Elixir.N)
cut(N)
iex(5)> Cater.cut(1)
** (FunctionClauseError) no function clause matching in Cater.cut/1
    iex:2: Cater.cut(1)

So, the reason you got the seemingly strange error message no function clause matching in Cater.cut/1 is that there's technically nothing wrong with your program (except for the actual function body of cut(N)—it would raise an ArithmeticError, which Elixir warns you about at compile time); it's perfectly valid, it just doesn't do what you wanted it to do/what you thought it did.