The pattern can never match the type - why is dialyzer throwin it in this case?

I found an interesting piece of code that doesn’t pass Dialyzer check in application I work on. I was able to narrow it down into a pretty simple example:

defmodule DialyzerFail do
  def call(condition, number) do
    with {:ok, :good} <- first_check(condition),
         {:ok, :nice} <- second_check(number) do
      true
    else
      error ->
        case error do
          {:error, :bad} ->
            false

          {:error, :ugly} ->
            notify_ugly(number)
            false
        end
    end
  end

  def first_check(condition) do
    if condition, do: {:ok, :good}, else: {:error, :bad}
  end

  def second_check(number) do
    if number > 1, do: {:ok, :nice}, else: {:error, :ugly}
  end

  def notify_ugly(_number), do: nil
end

For this code, Dialyzer fails with:

lib/test.ex:9:pattern_match
The pattern can never match the type.

Pattern:
{:error, :bad}

Type:
{:error, :ugly}

________________________________________________________________________________
lib/test.ex:12:pattern_match
The pattern can never match the type.

Pattern:
{:error, :ugly}

Type:
{:error, :bad}

Now, I understand how I could rewrite this code to both look better and appease Dialyzer, but I also would like to understands why it seems to not understand what’s going on in the example I gave.

2 Likes

Because of the way how with expands to nested case, each of them having the same clauses from else.

with and dialyzer are hard to get to agree with that test. Either do not use with or ignore all those errors individually.

1 Like

This sounds like an instance of this bug:

2 Likes