Fn conflict match: warning vs error

This example here gives a warning that the second definition will never match join/2.

defmodule Concat3  do  
  def join(a, b) do
    IO.puts "***First join"
    a <> b
  end

  def join(a, b, sep \\ " ") do
    IO.puts "***Second join"
    a <> sep <> b
  end
end

If we flip the definitions, we get an error:

defmodule Concat3  do
  def join(a, b, sep \\ " ") do
    IO.puts "***Second join"
    a <> sep <> b
  end

  def join(a, b) do
    IO.puts "***First join"
    a <> b
  end
end

Question: The fact these two definitions overlap is obvious. What is confusing to me is why it is an error in one case but a warning in another. There is something going on here that I am not understanding (and I do not have it pin pointed), but I would have expected it to be both warnings or both errors. What’s going on ?

EDIT: Context: last example on Modules and functions - The Elixir programming language

1 Like

Hi! It would be easier for us to help you understand this, if you’d give us the errors/warnings text as well.

Not everyone has access to an elixir compiler all the time, also the observed behaviour might be version dependant, therefore please also provide your elixir version.

1 Like

Good point. Here is:

Warning

cat warn.ex; elixir warn.ex 
defmodule Concat3  do

  def join(a, b) do
    IO.puts "***First join"
    a <> b
  end

  def join(a, b, sep \\ " ") do
    IO.puts "***Second join"
    a <> sep <> b
  end

end
warning: this clause for join/2 cannot match because a previous clause at line 3 always matches
  warn.ex:8

Error

cat error.ex ; elixir error.ex 
defmodule Concat3  do

  def join(a, b, sep \\ " ") do
    IO.puts "***Second join"
    a <> sep <> b
  end

  def join(a, b) do
    IO.puts "***First join"
    a <> b
  end

end
** (CompileError) error.ex:8: def join/2 conflicts with defaults from join/3
    error.ex:8: (module)

Version

elixir --version
Erlang/OTP 24 [erts-12.1.5] [source] [64-bit] [smp:24:24] [ds:24:24:10] [async-threads:1] [jit]

Elixir 1.12.3 (compiled with Erlang/OTP 24)

Let me know if anything else is needed.

1 Like

I‘d expect the difference to be that the conflict in the first case is caused by autogenerated code, while in the second case it‘s caused by code you wrote and therefore you need to fix.

From another angle: In the first case still both implementations are callable, even if the second function will only be callable with explicitly supplying the default. In the second example the latter function can never be called.

The pattern matching is attempted from top to bottom (order of definitions). So the first join definition is attempted at first, then the second one and so on.

Providing a default argument actually defines two functions:

defmodule Concat3 do
  def join(a, b, sep \\ " ") do
    IO.puts "***Second join"
    a <> sep <> b
  end
end

defines Concat3.join/3 and Concat3.join/2. Something like this:

defmodule Concat3 do
  def join(a, b, sep) do
    IO.puts "***Second join"
    a <> sep <> b
  end

  def join(a, b) do
    join(a, b, " ")
  end
end

So when you’re trying to define a separate join(a, b) manually, the function can never be matched or creates a conflict.

1 Like