brexhq/result

mask_error/2 causes pattern match type errors due to extraneous patterns

Opened this issue · 0 comments

Love the library, thanks for putting this out!

Describe the bug
When using mask_error/2, dialyzer will throw type errors for conditions which are handled by mask_error/2 but are extraneous based on the typing of the input value.

To Reproduce

case :random.uniform(3) do
  3 -> :ok
  2 -> {:ok, true}
  _ -> {:error, :test}
end
|> mask_error(:testing)

case :random.uniform(2) do
  2 -> :ok
  _ -> {:ok, true}
end
|> mask_error(:testing)

case :random.uniform(2) do
  2 -> :ok
  _ -> {:error, :test}
end
|> mask_error(:testing)

Running dialyzer will report the second and third calls to mask_error/2 as having a pattern_match error, as dialyzer determines that the second case statement will never produce {:error, _}, and the third case statement will never produce :ok.

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

Pattern:
{:error, _}

Type:
:ok | {:ok, true}

________________________________________________________________________________
/example.ex:18:pattern_match
The pattern can never match the type.

Pattern:
{:ok, 95}

Type:
:ok

Expected behavior
The typing of the input Result should not cause mask_error/2 to be flagged with pattern_match errors, as long as the input type is covered within Brex.Result.Base.t().

Additional context
I'm guessing this is because of how it's implemented as a macro. A similar implementation as a normal method does not throw a type error:

  @spec masq_error(Brex.Result.Base.t(a), b) :: 
          :ok | {:ok, a} | {:error, b} 
        when a: var, b: var
  def masq_error(a, b) do
    case a do
      :ok -> :ok
      {:ok, _} = v -> v
      {:error, _} -> {:error, b}
    end
  end

Most of the time, once I begin piping results around, they get normalized to {:ok, value} and {:error, value}, which means that mask_error/2 begins throwing these type errors all over the place =(. I haven't tested, but this same issue might also occur when using convert_error/3.