Would somebody please explain the said lines in this test?

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)>
3 Likes

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.

1 Like

Thank You @chulkilee @sergio @alexgaribay @yurko @voger @landric for replying!

Now it’s clear to me!

:heart_eyes:

3 Likes

May I just add that this topic is fine example of why is an Elixir community great? :slight_smile:

8 Likes

Another example, why Elixir core team and community are awesome!

1 Like

FYI: How do you refer to the Match Operator when reading code?

2 Likes

Great explanation! Thanks Voger.

2 Likes

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.

@shankardevy @voger

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.

1 Like

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.

1 Like

Thank you @peerreynders, thank you @voger!

Commenting on it 10 months later. :slight_smile:

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{}]

  • the pattern match is essentially [%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.
  • after the match is successful the names 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:

  • the pattern match is essentially [_, _] = Catalog.list_products i.e. a two element list where each element can be any data type - otherwise the match will fail.
  • after the match is successful the names 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

  • The match will only succeed if the first element of the list matches the current content of p1 and the second element matches the current content of p2 (and the list has exactly two elements).
1 Like

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

1 Like

Stripping it down to the bare essentials: = outside of a pattern is the match operator.
When you see a = 3

  • the _ = 3 match succeeds because _ can be anything
  • because the match succeeds a is bound to the value of the match: 3
2 Likes

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.

1 Like

You mean as he said,