Parashoe
Understanding the match operator
I’m not fully understanding how matching works with function arguments. In Functions and Pattern Matching it is explained (IMO poorly) that everything in the argument is matched independently. As a result this experiment I made works:
iex(6)> test = fn (%{a: x} = %{b: y}) -> IO.puts "x:" <> x <> " y:" <> y end
#Function<42.18682967/1 in :erl_eval.expr/6>
iex(7)> test.(%{a: "what", b: "happened"})
x:what y:happened
:ok
iex(8)>
But I don’t fully understand the semantics of this. Is the = here the same match operator as usual? I tested the match operator alone for this behavior and it does seem to match everything!
iex(16)> %{a: x} = %{b: y} = %{a: "what", b: "happened"}
%{b: "happened", a: "what"}
iex(17)> x
"what"
iex(18)> y
"happened"
iex(19)>
So it seems that the match operator is both associative and commutative which I did not expect. I also assume passing arguments is the same as including another match with the passed argument.
(this is correct)
Where is this documented? A quick search on the Elixir hexdocs of = and match aren’t fruitful. Pattern matching — Elixir v1.19.0-dev doesn’t say anything about it.
Edit: I think the article may just be wrong or poorly worded. I still need some clarification though
Thank you @LostKobrakai for clarifying things. The match operator always returns the right side value or fails with an error.
Marked As Solved
bjorng
A few years ago we in the compiler team at OTP started to get bug many bug reports about the Erlang compiler crashing when compiling some “strange” Erlang code. It turned out that the Erlang code that triggered the crashes was generated by a fuzzer, erlfuzz.
One of the classes of bugs in the Erlang compiler and in the documentation was in pattern matching. We had many discussions in the OTP team about how to best describe pattern matching and how to fix the compiler to be consistent and never crash.
The result was that we realized that there is not one match operator, but two distinct operators: the match operator and the compound match operator.
Here is the revised documentation about matching resulting from those bug reports and our discussions:
Also Liked
LostKobrakai
I’d start from some underlying primitives here and build up understanding from there:
- In elixir everything is an expression, which can be evaluated to a value – there are no statements or
voidreturn values.
E.g.x = if 1 < 2, do: :a, else: :bwill bindxwith:a - The value returned by a match expression is the value of the right hand side value in the match expression.
So for^y = (pattern = y)thepattern = ywill evaluate to the value ofy - In the above the binding of
patternis kind of a side effect as it doesn’t affect the return value of the match expression.
Therefore for the outer match expression^y = (pattern = y)can be simplified to^y = y - Being right associative match expressions can be chained without affecting the above properties even if you leave out explicit parenthesis. Each chained match expression will evaluate to the right most “input”, with all the individual match expressions having the chance to fail matching as well as resulting in variables being bound as side effects of their matching.
a = %{b: 1} = %{d: d} = %{b: 1, d: 4}and the following function essentially the same.
%{d: d} = %{b: 1, d: 4}
%{b: 1} = %{b: 1, d: 4}
a = %{b: 1, d: 4}
These properties make it essentially irrelevant in which order patterns are placed in a chained set of match expressions, though only the patterns, the right most value needs to stay on the right. Yes technically there is an order – you could move least permissive match patterns more right to match earlier than less permissive ones – but in practise that’s really irrelevant.
Now finally moving to function parameters. Usually these cause confusion because the actually imporant “right most input” doesn’t actually show up when writing a function head.
Having a function shows just a chained set of match patterns.
def my_function(a = %{b: 1} = %{d: d}), do: […]
When calling my_function(%{b: 1, d: 4}) internally you’ll essentially get these patterns evaluated as: a = %{b: 1} = %{d: d} = %{b: 1, d: 4}, adding the provided parameters to the right most side of the chain.
Hence in function heads you can reorder all parts of a chained match expression given the input isn’t present explicitly and it wouldn’t functionally change the function (beyond microoptimizations).
derek-zhou
Match is right associative.
So,
%{a: x} = %{b: y} = %{a: "what", b: "happened"}
is really:
%{a: x} = (%{b: y} = %{a: "what", b: "happened"})
Also, Map matched on partial keys, so, %{b: y} = %{a: "what", b: "happened"} is a match, resulting the full map.
D4no0
I think the article has pretty clear wording, for example if you have:
def hello(%{name: person_name} = person) do
IO.puts "Hello, " <> person_name
IO.inspect person
end
The value person will have the entire map passed as the argument binded to it and person_name will bind the value of the key name. This kind of match also enforces the argument to be a map and contain the name key.
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #elixirconf-us
- #advent-of-code
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #hex










