Maxximiliann

Maxximiliann

CompileError: "invalid use of _. "_" represents a value to be ignored in a pattern and cannot be used in expressions"

This case statement is perfectly functional, but it repeats code:

case foo do
  {:pass, _} = reason ->
    Logger.info(reason)

  {:ok, _, _} ->
    status = Keyword.get(opts, :status)
    some_func(status)

    Logger.info("Executing function: #{some_func} with status: #{status}")

  {:ok, :bosh} ->
    status = Keyword.get(opts, :status)
    some_func(status)

    Logger.info("Executing function: #{some_func} with status: #{status}")
end

To eliminate repetition, the following version was attempted, however it results in a CompileError: invalid use of _. "_" represents a value to be ignored in a pattern and cannot be used in expressions

case foo do
  {:pass, _} = reason ->
    Logger.info(reason)

  message
  when message == {:ok, _, _} or message == {:ok, :bosh} ->

    status = Keyword.get(opts, :status)
    some_func(status)

    Logger.info("Executing function: #{some_func} with status: #{status}")
end

What’s the best way to resolve this issue in a case statement? What alternate control flow structure, if any, would be more appropriate for this task?

Marked As Solved

eksperimental

eksperimental

You are breaking a few rules in your code.

  1. _ matches anything on the left side of a assignment, not on the right side, where you are defining your values.
  2. _ works with the = (match) operator not with the `==’ (comparison) operator.
  3. you cannot use the = operator in guards

Anyway, There are at least two ways of doing it, one with match? as @al2o3cr mentioned, another one with guards.

foo = {:ok, :bosh}

cond do
  match?({:pass, _}, foo) ->
    1

  match?({:ok, _, _}, foo) or match?({:ok, :bosh}, foo) ->
    2
end

case foo do
  {:pass, _} ->
    1

  foo
  when is_tuple(foo) and tuple_size(foo) == 2 and elem(foo, 0) == :ok
  when is_tuple(foo) and tuple_size(foo) == 3 and elem(foo, 0) == :ok and elem(foo, 1) == :bosh ->
    2

  # previous clause can be optimized like this which can be optimized like this (not so much readable as the previous one though)
  foo
  when is_tuple(foo) and elem(foo, 0) == :ok and
         (tuple_size(foo) == 2 or (tuple_size(foo) == 3 and elem(foo, 1) == :bosh)) ->
    2
end

There is a practical use for the cumbersome second solution, and that is GUARDS! you can define the guard with defguardand use it even in function definitions (since you asked about avoiding repetition)

  defguard is_foo(term)
           when is_tuple(term) and elem(term, 0) == :ok and
                  (tuple_size(term) == 2 or (tuple_size(term) == 3 and elem(term, 1) == :bosh))

case foo do
  {:pass, _} ->
    1

  foo when is_foo(foo)  ->
    2
end

Also Liked

soup

soup

Optimise for readability until it’s a bottleneck IMO.

e: disregard, see benches below

soup

soup

Actually thinking about it, the cond code isn’t very flexible, you’ll probably need to use match? in each clause.

I benched it as-is anyway:

code
x = {:ok, 1}

case_fn = fn ->
  case x do
    {:ok, _} -> :ok
    _ -> :error
  end
end

cond_fn = fn ->
  cond do
    {:ok, _} = x -> :ok
    true -> :error
  end
end

Benchee.run(
  %{
    "case" => case_fn,
    "cond" => cond_fn
  },
  time: 10,
  memory_time: 2
)
spoilers!
Operating System: Linux
CPU Information: Intel(R) Core(TM) i5-4670K CPU @ 3.40GHz
Number of Available Cores: 4
Available memory: 23.37 GB
Elixir 1.13.1
Erlang 24.2

Benchmark suite executing with the following configuration:
warmup: 2 s
time: 10 s
memory time: 2 s
parallel: 1
inputs: none specified
Estimated total run time: 28 s

Benchmarking case...
Benchmarking cond...

Name           ips        average  deviation         median         99th %
case        3.50 M        0.29 μs ±18914.94%       0.166 μs        0.58 μs
cond        0.76 M        1.32 μs  ±3154.19%        1.02 μs        1.90 μs

Comparison: 
case        3.50 M
cond        0.76 M - 4.61x slower +1.03 μs

Memory usage statistics:

Name    Memory usage
case         0.23 KB
cond         1.42 KB - 6.07x memory usage +1.19 KB

**All measurements for memory usage were the same**

cond performs “slightly” worse, at 4.6x slower and 6x more memory but that sounds like an Operations problem to me. Just buy a bigger instance :slight_smile:.

al2o3cr

al2o3cr

What version of Elixir are you running? Trying the code from your post in 1.12.0 produces a different compile error:

** (CompileError) main.exs:14: invalid use of _. "_" represents a value to be ignored in a pattern and cannot be used in expressions
    (stdlib 3.15.2) lists.erl:1358: :lists.mapfoldl/3
    (stdlib 3.15.2) lists.erl:1359: :lists.mapfoldl/3
    (stdlib 3.15.2) lists.erl:1358: :lists.mapfoldl/3
    (elixir 1.12.2) expanding macro: Kernel.or/2

I can get the illegal expression in guard, case is not allowed message if I use match?({:ok, _, _}, message) instead of == in the guard.

Where Next?

Popular in Questions Top

vertexbuffer
Hello, can anybody help here..? I have a list of players and I what to delete an element, but every for loop the list is reverting to ori...
New
marius95
Hello everyone, I try to use an Javascript Event Handler in my root.html.leex file. Therefore I created a function in the app.js file: ...
New
johnnyicon
Hi all, I’ve just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I’m trying to use Postgres...
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
Lily
In templates/appointment/index.html.eex: <%= for appointment <- @appointments do %> <tr> <td><%= appoi...
New
lucidguppy
I have a super simple question about elixir - how would I take a file like this foo bar baz and output a new file that enumerates th...
New
rms.mrcs
Hi, I need to transform a list of numbers into a map where the keys are the indexes and the values are the original values of the list. ...
New
komlanvi
Hi everyone, I was playing with phoenix liveView but I run into an issue. I have a form and want to validate each input text when the te...
New
shahryarjb
Hello, I get Persian date from my client and convert it to normal calendar like this: def jalali_string_to_miladi_english_number(persi...
New
New

Other popular topics Top

WestKeys
Currently suffering from paralysis by [HTTP client] analysis. This is rather unusual in Elixirland as there tends to be consensus on the ...
New
chrismccord
As promised, the first release candidate of Phoenix 1.3.0 is out! This release focuses on code generators with improved project structure...
New
JeremM34
Hello, how can I check the Phoenix version ? Thanks !
New
jononomo
I am trying to figure out how Mix knows whether the environment is test, dev, or prod – where is this set? Thanks.
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
gausby
I asked this very same question on twitter and got some interesting feedback, but I thought it would be a good question to ask here as we...
1207 39523 209
New
AngeloChecked
What learn first? Rust or Elixir Hi Elixir community! I’m here because i want learn a new language. I’m a junior developer and mainly i ...
New
fayddelight
I tried installing elixir 1.11.2 erlang 23.3.4 via asdf in my zsh shell. Enabled the versions locally and globally. When I list them ...
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