OK, that point is clear now, why doesn’t it work like the following?
assert %Product{} == p1
assert %Product{} == p2
OK, that point is clear now, why doesn’t it work like the following?
assert %Product{} == p1
assert %Product{} == p2
because %Product{}
and %Product{name: "Tomato"}
are not equal. Remember %Product{}
has default name: nil
. By doing
%Product{} = %Product{name: "Tomato"}
you check that the minimum matches. In this case both are %Product{}
. You don’t check for equality. You check for common attributes.
Here are more iex examples
iex(14)> %{name: "John"} = %{name: "John", age: 14000}
%{age: 14000, name: "John"}
iex(15)> %{name: "John", age: 3} = %{name: "John", age: 14000}
** (MatchError) no match of right hand side value: %{age: 14000, name: "John"}
iex(15)> %{} = %{name: "John", age: 14000}
%{age: 14000, name: "John"}
iex(16)> %{name: "John"} == %{name: "John", age: 14000}
false
iex(17)> %{} == %{name: "John", age: 14000}
false
iex(18)>
Double equals tests equality.
%Product{} = p1
…is “does whatever is in p1 fit the shape of a Product struct?”
%Product{} == p1
…is “is the empty Product struct exactly equal to whatever’s stored in p1?”
==
is reversible, like the equals sign you remember from algebra class.
Thank You @chulkilee @sergio @alexgaribay @yurko @voger @landric for replying!
Now it’s clear to me!
May I just add that this topic is fine example of why is an Elixir community great?
Great explanation! Thanks Voger.
Hi Shankar!
on the next page you combine
[p1, p2] = Catalog.list_products
assert %Product{} = p1
assert %Product{} = p2
into
[p1 = %Product{}, p2 = %Product{}] = Catalog.list_products
what if I write it like
[%Product{} = p1, %Product{} = p2] = Catalog.list_products
instea of the above?
By the way it passes even if I make it like that.
The actual pattern match is:
[%Product{}, %Product{}] = Catalog.list_products
i.e. the value of Catalog.list_products
is a two element list, each of which is a Product
struct. A successful match will simply evaluate to that two element list. To be clear:
[%Product{}, %Product{}]
is the pattern
Catalog.list_products
evaluates to the value being matched
So looking at
[%Product{} = p1, %Product{} = p2] = Catalog.list_products
p1
and p2
are names inside a pattern. So at this point =
is a mere a binding i.e. bind the name (e.g. p1
or p2
) to the sub-match (e.g. of %Product{}
). And because it’s a mere binding (not a match) it can also be written as:
[p1 = %Product{}, p2 = %Product{}] = Catalog.list_products
Furthermore:
iex(1)> a = 1
1
iex(2)> b = 2
2
iex(3)> [a,b] = [2,3]
[2, 3]
iex(4)> [^a,^b] = [3,4]
** (MatchError) no match of right hand side value: [3, 4]
iex(4)>
i.e. if a name is used inside a pattern for the purpose of matching against the value that it is already bound to then that name/value has to be pinned.
Pattern = Value
Generally speaking names inside a Pattern
are bound while pinned names are matched. The top-level =
has the Pattern
to the left and the Value
to the right. Any =
inside Pattern
is just a binding, not a pattern match.
So you meant writing
[p1 = %Product{}, p2 = %Product{}] = Catalog.list_product
or
[%Product{} = p1, %Product{} = p2] = Catalog.list_products
in the said test, is the same, right?
Yes. It doesn’t matter.
Commenting on it 10 months later.
I think you wanted to say "Any =
inside Pattern
(i.e., [p1 = %Product{}, p2 = %Product{}]
in this case) is just a binding pattern match, not pattern match binding.
note: Binding to me looks like assignment in non-functional languages like a = 3
and pattern matching looks like checking for equality like if (a == 3
) do this, else do that, (but not the strict equality), and in that sense I wrote the above comment.
With regards to [p1 = %Product{}, p2 = %Product{}]
[%Product{}, %Product{}] = Catalog.list_products
i.e. a two element list where each element has to be a Product
structure - otherwise the match will fail.p1
and p2
are bound to their values. p1
is bound to the first element in the list and p2
is bound to the second element in the list.Now consider [p1, p2] = Catalog.list_products
:
[_, _] = Catalog.list_products
i.e. a two element list where each element can be any data type - otherwise the match will fail.p1
and p2
are bound to their values. p1
is bound to the first element in the list and p2
is bound to the second element in the list.Finally [^p1, ^p2] = Catalog.list_products
p1
and the second element matches the current content of p2
(and the list has exactly two elements).@pillaiindu
What @peerreynders wants to say is that.
[p1 = %Product{}, p2 = %Product{}]
= Catalog.list_products
In the above pattern match, the main equal sign (in bold) is actually pattern matching (assigning) p1 and p2 to the elements resulted from the list_products
function, and the first two equal signs (inside the LHS list) are merely checking if the p1 and p2 after the assignment are actually Product structs or not.
Edit: Or may be it’s saying, assign the two elements to p1 and p2 only if they were Product structs. But no matter which way you think about it, the effect is the same.
Stripping it down to the bare essentials: =
outside of a pattern is the match operator.
When you see a = 3
_ = 3
match succeeds because _
can be anythinga
is bound to the value of the match: 3
Thank you!
I think I already got that idea of how that pattern match worked 10 months ago, but I’m a bit confused about the terms. The term bind and pattern match.
There are two pattern matches in the code [p1 = %Product{}, p2 = %Product{}] = Catalog.list_products
, which one he referred to in the above sentence.
Not the way I see it. There is one pattern - the list is part of the pattern. p1
and p2
want to be bound to specific sub-patterns if the overall match succeeds.
You mean as he said,