Fix definitions with multiple clauses and optional values that only have 1 param

I have the following code in my GenServer:

def init(%State{} = state), do: {:ok, state, 1_000}
def init(opts \\ []) do
  state = struct(State, opts)
  {:ok, state, 1_000}
end

This code, as some of you may guess will generate the following warning:

definitions with multiple clauses and default values require a header. Instead of:

def foo(:first_clause, b \\ :default) do ... end
def foo(:second_clause, b) do ... end

one should write:

def foo(a, b \\ :default)
def foo(:first_clause, b) do ... end
def foo(:second_clause, b) do ... end

def init/1 has multiple clauses and defines defaults in one or more clauses

The warning is pretty good, but I don’t see how I can apply it. Mainly because init can be called in 3 completely different ways:

  1. passing a state
  2. with no arguments
  3. passing an options keyword list

How can I fix this?

1 Like

:wave:

Just following the suggestion in the warning, maybe:

def init(opts \\ [])
def init(%State{} = state), do: {:ok, state, 1_000}
def init(opts) do
  state = struct(State, opts)
  {:ok, state, 1_000}
end

This is confusing to me. By doing it that way, I am saying that state is actually an opts, right ?

The problem is that it’s elixir creating multiple function heads for you here:

def init(), do: init([])
def init(%State{} = state), do: …
def init(opts), do: …

So it doesn’t really care about your patter match at all. It just needs to know that the first parameter is optional and shall be passed [] if not present. You could do:

def init(state_or_opts \\ [])
def init(%State{} = state), do: {:ok, state, 1_000}
def init(opts) do
  state = struct(State, opts)
  {:ok, state, 1_000}
end
4 Likes

Looking at your code, for me it seems much more like opts beeing a listified %State{}