That simply results in %{x: 5} being bound to a - i.e. it is a match that always succeeds, while a is subsequently never used (macro code from Kernel.match?/2).
Similar to:
a = %{a: 1}
match1 =
case %{x: 5} do
a -> true # i.e. "%{x: 5}" is bound to "a" - a subsequently unused name
_ -> false
end
# Semantically similar to above case using a plain match/bind.
match2 =
try do
a = %{x: 5} # i.e. "%{x: 5}" is bound to "a" - a subsequently unused name
true
catch
:error, {:badmatch, _} ->
false
# # --- rescue is PREFERRED in Elixir
# rescue
# _ in MatchError ->
# false
end
# https://elixir-lang.org/getting-started/try-catch-and-rescue.html#errors
#
# In Elixir, we avoid using try/rescue because WE DON’T USE ERRORS FOR CONTROL FLOW.
IO.puts("match?(a,{x: 5}) #{match1}")
IO.puts("alt_match?(a,{x: 5}) #{match2}")
$ elixir demo4.exs
warning: variable "a" is unused
demo4.exs:1
warning: variable "a" is unused
demo4.exs:5
warning: variable "a" is unused
demo4.exs:12
match?(a,{x: 5}) true
alt_match?(a,{x: 5}) true
$
as it simply verifies that all the necessary key/value pairs are present and will stop as soon as that is not the case.
In this case I also prefer to use fetch/2 in order the leverage the underlying map functionality rather than relying on in/2’s macro magic even if it makes the code a bit more verbose (so be it).
One thing to be aware of is that the empty set is a subset of any other set - the same is true for empty maps.
In a pattern matching, a left-hand side pattern is matched against a right-hand side term.
The phrasing suggests to me that patterns are very different from terms - patterns aren’t listed under Data Types.
Pinning ‘pins’ the binding - it expresses that no re-binding of that name should take place; at that point matching succeeds if both values are equal otherwise there will be a :badmatch error.
Erlang doesn’t need pinning because it doesn’t allow re-binding. But a simple name to the left of the match operator (=) means:
if the name is unbound, bind the name to the right hand side (and return the bound/matched value)
if the name is bound and the name is bound to a value equal to the right hand side, succeed (and return the matched value)
if the name is bound and the name is bound to a value not equal to the right hand side, fail (and throw a badmatch error)
FYI: Map is an Enumerable so Enum.all?(map1, &(&1 in map2)) should work - I just like to be explicit.
I see. Maybe the variable rebinding ability makes this a little confusing behaviour in Elixir.
You’re right, and I’ve noticed that you’re very explicit. That’s good, even more in examples, but I just wanted to show him he can write it in a shorter way.
Either would be hidden behind a function so concise/simple/explicit/verbose really doesn’t factor into it. I would hope that the fetch solution is pretty good performance/memory wise - but that could be totally wrong - one would have to benchmark the various solutions in a representative environment to actually know.
Conceptually it seems possible.
At this point I doubt it. I can’t find a source but (at least for me) everything seems to hint that patterns are fixed at compile time - so short of writing a module with the injected pattern and compiling and then loading it…
It may have been one of those tradeoffs that allowed the BEAM to heavily optimize pattern matching - but I’m just guessing.
Yeah it would make sense if a pattern is a compile-time construct in which case there would be no good way to use pattern matching for my problem as stated. Although for my current problem I am creating my map statically at compile time so I may be able to send my map to a macro and use that macro to pattern match against a map.
Crudely (… must be one of the simplest applications for macros I’ve come across):
defmodule Pattern do
defmacro from(pattern) do
quote do
unquote(pattern)
end
end
end
defmodule Demo do
require Pattern
@demo_map %{a: 1}
def subset?(super_map) do
case super_map do
Pattern.from(@demo_map) ->
true
_ ->
false
end
end
def subset2?(super_map) do
try do
Pattern.from(@demo_map) = super_map
true
catch
:error, {:badmatch, _} ->
false
end
end
def subset3?(Pattern.from(@demo_map) = _super_map), do: true
def subset3?(_), do: false
def a,
do: @demo_map
def with_fun(subset) do
fn {k,v} ->
IO.puts("a ⊆ #{Atom.to_string(k)}: #{subset.(v)}")
end
end
def with_fun({title, list}, fun) do
IO.puts(title)
Enum.each(list, with_fun(fun))
end
end
check_list = [a: Demo.a, b: %{a: 1, b: 2}, c: %{c: 3}, e: %{}]
Demo.with_fun({"--- subset? ", check_list}, &Demo.subset?/1)
Demo.with_fun({"--- subset2? ", check_list}, &Demo.subset2?/1)
Demo.with_fun({"--- subset3? ", check_list}, &Demo.subset3?/1)
$ elixir demo5.exs
--- subset?
a ⊆ a: true
a ⊆ b: true
a ⊆ c: false
a ⊆ e: false
--- subset2?
a ⊆ a: true
a ⊆ b: true
a ⊆ c: false
a ⊆ e: false
--- subset3?
a ⊆ a: true
a ⊆ b: true
a ⊆ c: false
a ⊆ e: false