It’s because of the declarative f/1 order.
When we write:
def f(a, b \\ [])
I fact the compiler will expand it in two functions:
def f(a), do: f(a, []) # <-- This is not a declarative f/1 it's the default-argument of f/2
def f(a, b) do: ...
So in the first case
defmodule A do
def f(a), do: "f(#{inspect(a)})" # <--- declarative f/1
def f(a, b \\ []) when is_list(b), do: ... # <--- will be expanded by the compiler
end
You will have two f/1 but with the declarative f/1 in first position (top-bottom) will match all clauses. So the compiler emit a warning for the unreachable expanded f/1 that handle the default-argument case of f/2.
In the second case:
defmodule A do
def f(a, b \\ []) when is_list(b), do ... # <--- will be expanded by the compiler
def f(a), do: "f(#{inspect(a)})" # <--- declarative f/1
end
The declarative f/1 is after the expanded f/1 but from the compiler’s perspective, the expanded f/1 is not a declarative f/1 function it’s the mechanism that handle the default-argument of the f/2 function. So when later the compiler encounter the declarative f/1 which is in conflict with the auto-generated f/1 it emit a compilation error.






















