Would somebody please explain the said lines in this test?

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,

No - that part I do not agree with.

BTW I got the concept of how the whole thing works, I got that when you people explained it 10 months ago, but I’m a bit confused about the terms bind and pattern-match. How these two terms are related in the context of your sentence I quoted above.

The way I view it: the match operator has a match phase and a bind phase.
Given [p1 = %Product{}, p2 = %Product{}] = Catalog.list_products

During the match phase the match succeeds if
[%Product{}, %Product{}] = Catalog.list_products
is satisfied - otherwise the match fails (this = is the match operator).

Only if the match succeeds do we move to the bind phase. We slap a name on the data that is the first list element and that name is p1 and we also slap a name on the data that is the second list element and that name is p2 (here the = (inside the pattern) simply identifies the data that the name wants to be associated with).

So “binding” is essentially slapping a name on a piece of data (a “value”). “Rebinding” rips the name off of one piece of data like a label and slaps it onto another piece of data. Data is immutable so it never moves - it’s the name that is moving.

2 Likes

Thank you! :slight_smile:

You explained it in a very nice way.

Thank you everyone!

I think, I have to write the summary of all the discussion about this last piece of code we discussed in the end, removing all the repeated questions etc, and then ask for Jose’s comment on that.

1 Like