Nicd

Nicd

Odd match warnings when compiling

I am getting odd warnings about pattern matching when compiling the following code (it is from a Phoenix controller, but as the warning doesn’t have anything to do with Phoenix, I posted this in the generic questions section):

  # Edit case for editing user's password
  def do_edit(conn, %{
        "user" =>
          %{
            "type" => "password",
            "old_password" => old_password,
            "password" => _
          } = params
      }) do
    user = AuthUtils.get_current_user(conn)
    password_changeset = User.password_changeset(user, params)

    with {:old_pass, true} <- {:old_pass, AuthUtils.check_user_password(user, old_password)},
         {:updated, %User{}} <- {:updated, AuthUtils.update_user(password_changeset)} do
      conn
      |> put_flash(:success, "Password changed.")
      |> redirect(to: Routes.preferences_path(conn, :edit))
    else
      err ->
        error_changeset =
          case err do
            {:old_pass, false} ->
              # We need to add an action to the changeset so that Phoenix will display the error,
              # otherwise it will think the changeset was not processed (as it has not been passed
              # to any Repo call) and will not show the errors
              %{password_changeset | action: :update}
              |> Ecto.Changeset.add_error(:old_password, "does not match your current password")

            {:updated, cset} ->
              cset
          end

        conn
        |> common_edit_assigns()
        |> put_flash(:error, "Error changing password.")
        |> render("preferences.html", pass_changeset: error_changeset)
    end
  end

The case statement inside the with’s else block gets the following warnings:

warning: this clause cannot match because of different types/sizes
  lib/code_stats_web/controllers/preferences_controller.ex:67

warning: this clause cannot match because of different types/sizes
  lib/code_stats_web/controllers/preferences_controller.ex:74

The lines refer to the {:old_pass, false} -> and {:updated, cset} ->. But I know that they do match and the code works. The function AuthUtils.check_user_password/2 returns a boolean, and AuthUtils.update_user/1 returns either a User struct or a changeset.

If I hover over the clauses in VSCode, it tells me that the first clause can never match {:updated, <changeset>} and the second clause can never match {:old_pass, false}, but that makes no sense to me (as the opposite clauses match those!).

Now to be clear, I’m not looking for advice how to structure the code better. I am simply wondering why the warnings are given. I cannot see the problem in the match.

Marked As Solved

OvermindDL1

OvermindDL1

That’s one of those with bugs, I get a LOT of those warnings, so many that I’ve ended up stripping out almost all of with from my project and going back to other libraries and patterns because of that as well as two other annoying issues I have with it. There is a still-open github issue about it at:

Also Liked

Nicd

Nicd

In this case it is not so easy to read because I haven’t refactored it into a nicer format yet. But sometimes you need to do many things in succession and handle all of their failures. Then with helps you to avoid a pyramid of nested conditionals. I know my code is not the best example but here is one with that has more things going on:

    with {:ok, %DateTime{} = datetime} <- parse_timestamp(timestamp),
         {:ok, datetime} <- check_datetime_diff(datetime),
         {:ok, offset} <- get_offset(timestamp),
         {:ok, %Pulse{} = pulse} <- create_pulse(user, machine, datetime, offset),
         {:ok, inserted_xps} <- create_xps(pulse, xps) do
      # Broadcast XP data to possible viewers on profile page and frontpage
      ProfileChannel.send_pulse(user, %{pulse | xps: inserted_xps})

      # Coordinates are not sent for private profiles
      coords = if user.private_profile, do: nil, else: GeoIPPlug.get_coords(conn)
      FrontpageChannel.send_pulse(coords, %{pulse | xps: inserted_xps})

      conn |> put_status(201) |> json(%{ok: "Great success!"})
    else
      {:error, :not_found, reason} ->
        conn |> put_status(404) |> json(%{error: reason})

      {:error, :generic, reason} ->
        conn |> put_status(400) |> json(%{error: reason})

      {:error, :internal, reason} ->
        conn |> put_status(500) |> json(%{error: reason})
    end

Here you can see the happy path is easily readable and the error handling is after it, and there is no nesting.

pedromvieira

pedromvieira

Is there any extra benefits to use with syntax? IMHO it’s just hard to read compared with regular syntax.

Where Next?

Popular in Questions Top

yawaramin
In the Dialyzer docs ( http://erlang.org/doc/man/dialyzer.html#requesting-or-suppressing-warnings-in-source-files ), there is a way to tu...
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New
joaquinalcerro
Hi there, I am working with Ecto-Postgresql and I need to call all of the records from a specific table but the table has 40,000 record...
New
vertexbuffer
Hello, can anybody help here..? I have a list of players and I what to delete an element, but every for loop the list is reverting to ori...
New
stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New
WestKeys
Currently suffering from paralysis by [HTTP client] analysis. This is rather unusual in Elixirland as there tends to be consensus on the ...
New
Qqwy
Original source of discussion: This topic on the Pragmatic Programmers' Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New
siddhant3030
Hi, I have to write a raw query for one of my project. But till now I have used ecto queries and don’t have much experience writing raw ...
New
idi527
I’ve been re-reading swift book again and noticed that multiline strings there don’t have a trailing line break, unlike in elixir iex(2)...
New

Other popular topics Top

JDanielMartinez
Hi! May someone helps me, please! I have two apps into an umbrella project: the first one is Database, which manages queries, and the se...
New
Brian
What is the proper way to load a module from a file in to IEX? In the python world, doing something like this pretty standard: from ....
New
JakeBecker
TL;DR: I’ve just released an implementation of Microsoft’s IDE-independent Language Server Protocol for Elixir. It adds language support ...
1140 51847 244
New
William
I would like to know that is there any online source for learning Phoenix Framework for building E-Commerce Store? Any advantage on build...
New
bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
New
lastday4you
I wanted to check elixir version in phoenix because i found that my elixir is 1.5 but when i use Enum.chunk_by it said the function is un...
New
ovidiubadita
Hey all, I discovered Elixir and I love it. I always wanted to learn a functional programming and I intended to go for Haskell, but afte...
New
electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID&lt;0.412.0&gt; terminating ** (Postgrex.Error) FATAL...
New
AstonJ
We’ve put together this wiki for Phoenix LiveView - please feel free to add any info you feel is worth including. What is Phoenix LiveV...
New

We're in Beta

About us Mission Statement