@spec for multiple functions

Hello! I have a question about @spec for functions with multiple clauses. Should there be one @spec definition for each function/arity? Or should there be a @spec for each function declaration?

For example is this desireable?

@spec foo({atom, any}) :: any
def foo({:ok, x}), do: x
def foo({:good, x}), do: x
def foo({:error, x}), do: x

or is this better?

@spec foo({:ok, any}) :: any
def foo({:ok, x}), do: x

@spec foo({:good, any}) :: any
def foo({:good, x}), do: x

@spec foo({:error, any}) :: any
def foo({:error, x}), do: x

@vrod Depends …

For example dialyzer does not support it and therefore returns a warning about overlapping domains, see:

On the other side it looks much better when looking at generated documentation.

Finally there is 3rd way for your use case. You can write something like:

@spec foo({:ok | :good | :error, any}) :: any
def foo({:ok, x}), do: x
def foo({:good, x}), do: x
def foo({:error, x}), do: x
1 Like

Most tools expect a single @spec per function at a particular arity.

That said, the most specific typespec that still covers all your inputs is usually the most useful one. Thus, you might want to use @spec foo({:ok | :good | :error, any}) :: any (as @Eiji already mentioned) ,
or, for this particular example even @spec foo({:ok | :good | :error, type}) :: type when [type: any] that indicates that the result value will be of the same type as the value passed in.

Thank you this makes sense!