elixirdialyzer

Elixir Dialyzer error when accessing struct prop using dot syntax


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)


Solution

  • 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.