About ambiguity introduced in function default arguments

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.