Having updated my project to use Elixir 1.12 I've noticed Dialyzer complaining about certain places where I've accessed the property of a struct using dot notation. For example here's a graphql resolver function in my app:
def update(_root, %{input: input}, %{context: %{current_user: user}}) do
case user
|> User.update_changeset(input)
|> Repo.update() do
{:ok, updated_user} ->
if user.email !== updated_user.email do
Email.email_changed(user.email, updated_user.email)
|> Mailer.deliver_later()
end
{:ok, updated_user}
err ->
err
end
end
Dialyser has highlighted the expression user.email !== updated_user.email
with the following error:
The call _.'email'/() requires that _@1 is of type atom(), not {map(), map()}
Any idea what this error means and how to fix it?
(this all compiles and runs fine, I'm just keen to learn why it doesn't seem to satisfy Dialyzer)
While it’s hard to tell whether dialyzer is wrong, or your code has glitches inducing this issue, without seeing how context is built before passing to this function, there is a bit of general advice.
Use deep pattern matching everywhere to narrow the issue.
def update(_root, %{input: input},
%{context: %{current_user: %User{email: email}}}) do
user
|> User.update_changeset(input)
|> Repo.update()
|> case do
{:ok, %User{email: ^email} = user} ->
{:ok, user}
{:ok, %User{email: updated_email} = user} ->
email
|> Email.email_changed(updated_email)
|> Mailer.deliver_later()
{:ok, user}
err ->
err
end
end
That way the code is cleaner, the intent is clearer, and the error (if happens) is more evident and glued to where it actually belongs.