Elixir Dialyzer error when accessing struct prop using dot syntax

293 views Asked by At

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)

1

There are 1 answers

4
Aleksei Matiushkin On BEST ANSWER

While it’s hard to tell whether 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.