ilya-kolpakov

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

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

benwilson512

Author of Craft GraphQL APIs in Elixir with Absinthe

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

idi527

Or !!%A{}.a … looks a bit like swearing.

Where Next?

Popular in Discussions Top

andre1sk
A big advantage to Elixir is all the distributed goodness but for many applications running on multiple nodes having integrated Etcd, Zoo...
New
jswny
I would like to better understand what the advantages/disadvantages of umbrella applications are compared to structuring your app as as s...
New
sashaafm
I’m trying to evaluate the best combo/stack for a BEAM Web app. Right now I’m exploring Yaws a bit, after having dealt with Phoenix for a...
New
arpan
Hello everyone :wave: Today I am very excited to announce a project that I have been working on for almost 3 months now. The project is...
New
pillaiindu
I want to convert a Phoenix LiveView CRUD website to a CRUD mobile app. What do you think is the easiest way to do so?
New
AstonJ
Please see the new poll here: Which code editor or IDE do you use? (Poll) (2022 Edition) It’s been a while since we first asked this, I...
208 31307 143
New
New
shishini
I think this twitter post and youtube video didn’t get as much attention as I hoped I am still new to Elixir, so can’t really judge ...
New
AstonJ
Can you believe the first professionally published Elixir book was published just 8 years ago? Since then I think we’ve seen more books f...
New
pdgonzalez872
If this has been asked here before, please point me to where it was asked as I didn’t find it when I searched the forum. Maybe a mailing ...
New

Other popular topics Top

AstonJ
Posting this to see if we can make things easier for people to get into Neovim. If you use Neovim and have a favourite distro please let ...
New
lastday4you
I wanted to check elixir version in phoenix because i found that my elixir is 1.5 but when i use Enum.chunk_by it said the function is un...
New
Nvim
Anybody knows a comprehensive comparison of Django and Phoenix, thanks for the help. Where are they similar? Where do they differ the m...
New
skosch
To my knowledge, put_in, Map.update etc. all have the one limitation of not automatically creating intermediate keys when needed (for exa...
New
pmjoe
I have a relationship of love and hate with Elixir. Lots of things are just absolutely right, but there are some things that are kind of ...
New
josevalim
Hi everyone, One of the features added to Elixir early on to help integration with Erlang code was the idea of overridable function defi...
New
nobody
Hi! In PHP: $_SERVER[‘SERVER_ADDR’] - in Elixir? Searched the docs for ip address and the web, no good results. Thanks!
New
jason.o
In the code below, if the create action is not set to accept “extra_key” as an input, it errors out with a message shown above. Is there ...
New
KronicDeth
Elixir plugin for JetBrain’s IntelliJ Platform (including Rubymine) This is a plugin that adds support for Elixir to JetBrains IntelliJ...
289 36432 110
New
nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New

We're in Beta

About us Mission Statement