elixirdialyzer

Dialyzer simplifies long union types


I've noticed that Dialyzer possibly simplifies long union types, at least when it comes to atoms.

For example, if I have:

defmodule Foo do
  @type name() :: :one | :two | :three

  @spec bar(name()) :: String.t()
  def bar(name), do: to_string(name)
end

and then I call the function with

Foo.bar(:hello)

Dialyzer will catch the error saying that :hello is a wrong type for this function.

But then, if I change the type declaration to make the union consist of more than 13 (my observation) items (pseudo code):

@type name() :: :one | :two | :three ... :fourteen

Dialyzer seems to convert it into a simple atom() type under the hood and would not complain about calling Foo.bar/1 with any atom, even if it's not included into the union.

So I wonder if that's expected behaviour and whether there's a way to prevent that?


Solution

  • I couldn't find a mention of it in the documentation, but this behavior is mentioned in the Success Typing paper explaining dialyzer:

    We allow for any disjoint union, including unions of singleton types such as 1 ∪ 2. Since these unions can become large or even infinite, in our analysis we impose a fixed size limit after which the union is widened to a supertype. For example, if the union limit is three the union type 1 ∪ 2 ∪ 3 ∪ 4 will be widened to integer ().

    Unfortunately, there doesn't seem to be any option to change this limit as of today.