I’m new to Elixir\Phoenix, and am currently working my way through the Programming Phoenix book.
Could someone please help me understand the following line of code: ‘{:error, %Ecto.Changeset{} = changeset} ->’, the complete function is below.
I get that the Accounts.create_user(…) is returning a tuple, and the CASE is matching the tuple, to either {:ok, user} or {:error, %Ecto.Changeset{} = changeset}.
But I don’t get why the empty structure ‘%Ecto.Changeset{}’, part of the second tuple. Could we have written: ‘{:error, changes}’. Most examples I’ve seen have a var in left hand side, i.e. %Ecto.Changeset{var: var} = changeset, and so I understand we are able to use assigned ‘var’ in our function, but I don’t know why the empty struct. Is it to ensure the second arg is an %Ecto.Changeset{}, or is there more?
def create(conn, %{"user" => user_params}) do
case Accounts.create_user(user_params) do
{:ok, user} ->
conn
|> put_flash(:info, "User: #{user.name} successfully created")
|> redirect(to: Routes.user_path(conn, :index))
**{:error, %Ecto.Changeset{} = changeset} ->**
render conn, "new.html", changeset: changeset
end
end
defmodule Dog do
defstruct [:name, :age]
end
defmodule My do
def go(%Dog{}), do: IO.puts "matched a Dog struct"
def go(%{}), do: IO.puts "matched a map"
def go(_), do: IO.puts "matched something else"
def test do
My.go(%{a: 1, b: 2})
My.go(%Dog{name: "Ralph", age: 3})
My.go(10)
end
end
In iex:
~/elixir_programs$ iex a.exs
Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
Interactive Elixir (1.6.6) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> My.test()
matched a map
matched a Dog struct
matched something else
:ok
iex(2)>
You should be aware that a struct will also match %{}, i.e. a map, so the order of the go() clauses is important.
Could we have written: ‘{:error, changes}’.
Yes, but that is pattern allows a more permissive match because changes is a variable and as such it will match anything.
Most examples I’ve seen have a var in left hand side, i.e. %Ecto.Changeset{var: var} = changeset,
The %Ecto.Changeset{var: var} pattern requires two things to be true for a match:
The thing on the right of the match operator, =, (that’s right it’s not an assignment operator like in other languages–it’s more powerful in elixir) has to be an Ecto.Changeset struct.
AND
The struct has to have the key :var.
If there is a match, then the var variable:
:var key (an atom)
|
V
%Ecto.Changeset{var: var}
^
|
var variable
will be assigned the value corresponding to the :var key in the struct. That type of pattern is used to extract values from a struct. That is not the intent/need of the original pattern.
Accounts.create_user(user_params) is a context function that could return an {:error, reason} tuple for any number of reasons. In {:error, %Ecto.Changeset{}}%Ecto.Changeset{} is only one possible reason but that is the particular reason the case clause is intended to handle.
%Ecto.Changeset{} = changeset simply binds that empty structure to the name changeset for reuse.
Precisely that. In pattern matching, an “empty map” %{} pattern represents any map. An “empty struct” %StructName{} pattern checks if the struct is of this type (this pattern is the same as %{__struct__: StructName}).
So if there’s another {:error, value} value than a Ecto.Changeset, this code will raise. If you don’t pattern match on %Ecto.Changeset{} here, there’s a chance you’d pass something else to render.
Yes, the reason is as you suspect so you can guarantee the second element in the tuple is of a specific type.
The only extra bit of context you might find helpful is that other functions may still return an error tuple without a changeset but this code only cares if it’s a changeset error.
Esp for someone coming from a Java\OO background, these concepts are quite new, and your replies have given my limited understanding some further depth.