Flatten Array, Analyzer comment, multiple arity, public interface

Is the analyzer making a mistake? The flatten/2 has to be public!

This is the solution:

  def flatten(list, acc \\ [])
  def flatten([], acc), do: Enum.reverse(acc)
  def flatten([nil | tail], acc), do: flatten(tail, acc)
  def flatten([head | tail], acc) when is_list(head), do: flatten(head ++ tail, acc)
  def flatten([head | tail], acc), do: flatten(tail, [head | acc])

This is the comment the analyzer provided:

I changed the code:

  def flatten(list, acc \\ [])
  defp flatten([], acc), do: Enum.reverse(acc)
  defp flatten([nil | tail], acc), do: flatten(tail, acc)
  defp flatten([head | tail], acc) when is_list(head), do: flatten(head ++ tail, acc)
  defp flatten([head | tail], acc), do: flatten(tail, [head | acc])

But I get this error:

exercism/elixir/flatten-array$ mix test
Compiling 1 file (.ex)
    error: defp flatten/2 already defined as def in lib/flatten_array.ex:20
    │
 21 │   defp flatten([], acc), do: Enum.reverse(acc)
    │        ^
    │
    └─ lib/flatten_array.ex:21:8

I believe that the arity is the core of the issue here. The public interface expects flatten to be a arity-1 function, but using def flatten(list, acc \\ []) defines both a arity-1 and a arity-2 function.

I tend to agree with the analyzer, the acc argument is an implementation detail, it shouldn’t be public.

To fix this, I would probably write def flatten(list), do: do_flatten(list, []) and make do_flatten private. Keeping the flatten name and making the arity-2 branch private might work too.

3 Likes