Hi,
I’m trying to get hang of specs, but I’ve hit a wall. I’m writing a thin facade over Nerves’ SystemRegistry, basically stuff like:
defmodule ConfigRegistry do
@spec update(SystemRegistry.scope(), any) :: {:ok, {map, map}} | {:error, term}
def update(scope, value) do
SystemRegistry.update([:config | scope], value)
end
end
Running mix dialyzer
on that results in
lib/config_registry.ex:2:invalid_contract
Invalid type specification for function.
Function:
ConfigRegistry.update/2
Success typing:
@spec update([any()], _) :: %SystemRegistry.Transaction{
:delete_nodes => MapSet.t(_),
:deletes => MapSet.t(_),
:key => _,
:opts => Keyword.t(),
:pid => pid(),
:update_nodes => MapSet.t(_),
:updates => map()
}
A quick look at
tells me there are two update
s, one that can be used standalone (this is the one I’m interested in), and the other one for use with an open transaction.
The functions are specced like this:
@spec update(one, scope, value :: any) :: Transaction.t() when one: Transaction.t()
@spec update(one, value :: any, opts :: Keyword.t()) ::
{:ok, {new :: map, old :: map}} | {:error, term}
when one: scope
I can sort of understand dialyxir’s complaint, if I make an assumtion that it cannot recognize [:config | scope]
as something that is not a SystemRegistry.Transaction.t()
. But that assumption seems very wrong.
So… Either (and most likely) I’m missing some crucial and basic knowledge of spec, there is an error in SystemRegistry.update
spec, or a bug in Dialixyr.
How can I persuade Dialyxir my code will always call the standalone update
function?