I’m confused… when grooming the docs for https://elixir-lang.org/getting-started/typespecs-and-behaviours.html I got the feedback that multiple function clauses require multiple @spec
s. But now that I’m digging into dialyzer, I’m getting errors in places where I’ve done that. E.g.
Overloaded contract for MyApp.some_function/1 has
overlapping domains; such contracts are currently unsupported and
are simply ignored.
Consider this module:
defmodule MyApp.Helpers do
alias Absinthe.Blueprint.Input.String, as: AbsintheString
alias Absinthe.Blueprint.Input.Integer, as: AbsintheInteger
alias Absinthe.Blueprint.Input.Float, as: AbsintheFloat
@spec parse_value(AbsintheString.t() | AbsintheInteger.t() | AbsintheFloat.t() | any()) :: {:ok, String.t()} | {:error, any()}
def parse_value(%AbsintheString{value: value}) do
{:ok, value}
end
def parse_value(%AbsintheInteger{value: value}) do
{:ok, Integer.to_string(value)}
end
def parse_value(%AbsintheFloat{value: value}) do
{:ok, Float.to_string(value)}
end
def parse_value(_) do
{:error, "Invalid value"}
end
end
Dialyzer seems happy when it has only a single combined @spec
, but readability is better when each function clause has its own @spec
, something more like this:
@spec parse_value(AbsintheString.t()) :: {:ok, String.t()}
def parse_value(%AbsintheString{value: value}) do
{:ok, value}
end
@spec parse_value(AbsintheInteger.t()) :: {:ok, String.t()}
def parse_value(%AbsintheInteger{value: value}) do
{:ok, Integer.to_string(value)}
end
# ... etc...
Which way is correct?