ilya-kolpakov
Structs: pattern matching on nil values
I think the behavior of pattern matching on null-valued pairs within structs is surprising:
iex(6)> defmodule A do defstruct [:a, :b] end
iex(7)> match?(%A{a: a}, %A{})
true
iex(8)> match?(%{a: a}, %{})
false
I would argue this is not very useful. By matching on the struct itself (e.g. on %A{}) we already know the keys that are going to be present. Hence pattern-matching on members provides zero additional value.
IMO, it would be more useful if the above struct match returned false. Indeed, it would provide a way to test a semantical presence of a value rather than a mechanical one. This can be done via guards but the syntax is more verbose.
Most Liked
michalmuskala
This is not true. At least not on today’s BEAM. Matching on <<_:binary>> is going to be faster, if you’re doing a binary pattern match already, to keep the “single match context” optimization. But if you’re only checking if something is a binary, it’s going to be slower. This is primarily because it needs to allocate the match context (which creates garbage on the heap that will lead to sooner garbage collection), while checking is_binary does not involve any memory movements. This memory access (and GC jitter) can be seen in the increased deviation of the benchmark.
Operating System: macOS
CPU Information: Intel(R) Core(TM) i7-4770HQ CPU @ 2.20GHz
Number of Available Cores: 8
Available memory: 16 GB
Elixir 1.6.0-dev
Erlang 20.1
Benchmark suite executing with the following configuration:
warmup: 2 s
time: 5 s
parallel: 1
inputs: binary, not binary
Estimated total run time: 28 s
##### With input binary #####
Name ips average deviation median 99th %
guard 436.27 K 2.29 μs ±34.28% 2.10 μs 4.20 μs
match 236.41 K 4.23 μs ±1391.73% 4 μs 11 μs
Comparison:
guard 436.27 K
match 236.41 K - 1.85x slower
##### With input not binary #####
Name ips average deviation median 99th %
guard 448.69 K 2.23 μs ±35.38% 2 μs 4.40 μs
match 431.08 K 2.32 μs ±43.81% 2.20 μs 4.60 μs
Comparison:
guard 448.69 K
match 431.08 K - 1.04x slower
The benchmark code:
defmodule Bench do
def match(<<_::binary>>), do: :binary
def match(_), do: :not_binary
def guard(binary) when is_binary(binary), do: :binary
def guard(_), do: :not_binary
def loop(0, _fun) do
:ok
end
def loop(n, fun) do
fun.()
loop(n - 1, fun)
end
end
jobs = %{
"match" => fn input -> Bench.loop(100, fn -> Bench.match(input) end) end,
"guard" => fn input -> Bench.loop(100, fn -> Bench.guard(input) end) end
}
inputs = %{
"binary" => "foo",
"not binary" => 123
}
Benchee.run(jobs, inputs: inputs)
benwilson512
It provides tons of value if you include a pattern on those members, or if you include those members in a guard.
Let’s filter all As that are empty
as |> Enum.filter(&match?(%A{a: []}, &1))
Ultimately the whole point of match? is that it works exactly like:
case value do
%A{a: a} -> true
_ -> false
end
If you want some other characteristic then you want some other function.
We are more than happy to try to help provide the most idiomatic way to achieve whatever your goal is in this situation. Just understand that suggestions that involve breaking changes to core functions are unlikely to gain a lot of traction.
idi527
Or !!%A{}.a … looks a bit like swearing.







