defguardp is_noretry(err)
when (is_atom(err) and err in @noretry_errors) or
(is_map(err) and is_map_key(err, :__struct__) and
:erlang.map_get(:__struct__, err) in @noretry_errors)
defguard is_retryable(err) when not is_noretry(err)
@noretry_errors is a list of modules.
I call it like this some_atom when is_retryable(some_atom) -> ...
This is the dialyzer error This is the compilation warning, dialyzer is fine :
warning: expected Kernel.is_map_key/2 to have signature:
:__struct__, atom() -> dynamic()
but it has signature:
dynamic(), %{optional(dynamic()) => dynamic()} -> dynamic()
in expression:
# .../backend_base.ex:47
is_map_key(some_atom, :__struct__)
It looks like it complains about is_map_key being called on an atom, although it does not happen because of the is_map(err) call in the guards.
defmodule SomeGood do
defstruct dummy: nil
end
defmodule SomeBad do
defstruct dummy: nil
end
defmodule Demo do
@good_modules [Somegood]
defguard is_good(mod_or_struct)
when (is_atom(mod_or_struct) and mod_or_struct in @good_modules) or
(is_map(mod_or_struct) and is_map_key(mod_or_struct, :__struct__) and
:erlang.map_get(:__struct__, mod_or_struct) in @good_modules)
end
defmodule Worker do
import Demo
def work(%contract{} = data) when is_good(contract) do # <------------- WARN
:alright
end
def work(_) do
:nope
end
def a_case(data) do
case data do
%contract{} when is_good(contract) -> :good_contract # <----------- WARN
data when is_good(data) -> :good_data
_other -> :bad_data
end
end
end
defmodule Client do
def run do
good_data = %SomeGood{}
bad_data = %SomeBad{}
{Worker.work(good_data), Worker.work(bad_data)}
end
end
This sounds like a bug. Yes those types are incompatible, but it’s only one branch of two.
warning: incompatible types:
atom() !~ map()
in expression:
# lib/test.ex:34
is_map(contract)
where "contract" was given the type atom() in:
# lib/test.ex:34
%contract{}
where "contract" was given the type map() in:
# lib/test.ex:34
is_map(contract)
Conflict found at
lib/test.ex:34: Worker.a_case/1