# ETS - conditional match with variable in Elixir

**URL:** <https://forum.elixirforum.com/t/ets-conditional-match-with-variable-in-elixir/52010>\
**Category:** Questions\
**Tags:** ets, troubleshooting\
**Created:** [November 24, 2022, 3:45pm UTC](https://forum.elixirforum.com/t/ets-conditional-match-with-variable-in-elixir/52010 "2022-11-24T15:45:10Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![ndrean](https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/ndrean/32/36680_2.png) [@ndrean](https://forum.elixirforum.com/u/ndrean)\
**Post date:** [November 24, 2022, 3:45pm UTC](https://forum.elixirforum.com/t/ets-conditional-match-with-variable-in-elixir/52010/1 "2022-11-24T15:45:10Z")

</div>

I want to query an ETS table based on a condition. As per [Erlang’s doc](https://www.erlang.org/doc/man/ets.html#fun2ms-1), I should be able to use variables in scope.  
Is it possible to formulate the query below with this “imported variable” in Elixir ?

```elixir
def get(id) do
  fun = :ets.fun2ms(fn {x,y,z,:_} when x == id or y == id -> {x,y,z} end)
  :ets.select(:table, fun)
end

```

This does not work with a variable, but works with a value.

```elixir
(FunctionClauseError) no function clause matching in :ms_transform.normalise/1

```

You can obtain the result as below but not satisfied

```elixir
def get(id), do:
[
    :ets.match_object(:table, {:"$1", id, :"$2", :_})
      | :ets.match_object(:table, {:"$1", :"$2", id, :_})
]
|> List.flatten()

```

---

<div class="post-metadata">

**Author:** ![LostKobrakai](https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/lostkobrakai/32/3072_2.png) [@LostKobrakai](https://forum.elixirforum.com/u/LostKobrakai)\
**Post date:** [November 24, 2022, 3:59pm UTC](https://forum.elixirforum.com/t/ets-conditional-match-with-variable-in-elixir/52010/2 "2022-11-24T15:59:33Z")

</div>

I’m not sure why exactly, but `:ets.fun2ms` doesn’t work in elixir (or at least not in all contexts). There’s `:ex2ms` to solve this:

```elixir
import Ex2ms

fun do {x,y,z,_} when x == 1 or y == 1 -> {x,y,z} end
# [
# {{:"$1", :"$2", :"$3", :_}, [{:orelse, {:==, :"$1", 1}, {:==, :"$2", 1}}],
# [{{:"$1", :"$2", :"$3"}}]}
# ]

```

Replace `1` with `id`.

---

<div class="post-metadata">

**Author:** ![ndrean](https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/ndrean/32/36680_2.png) [@ndrean](https://forum.elixirforum.com/u/ndrean)\
**Post date:** [November 24, 2022, 4:14pm UTC](https://forum.elixirforum.com/t/ets-conditional-match-with-variable-in-elixir/52010/3 "2022-11-24T16:14:27Z")

</div>

Yes this produces the “desired” 🙂 pattern. Thks

```elixir
q = [{
  {:"$1", :"$2", :"$3", :_},
  [{:orelse, {:==, :"$2", {:const, id}}, {:==, :"$3", {:const, id}}}],
  [{{:"$1", :"$2", :"$3"}}]
}]

```

with:

```elixir
q =
   Ex2ms.fun do
        {ch, e, r, _} when e == ^id or r == ^id -> {ch, e, r}
   end

```

---

<div class="post-metadata">

**Author:** ![al2o3cr](https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/al2o3cr/32/3457_2.png) [@al2o3cr](https://forum.elixirforum.com/u/al2o3cr)\
**Post date:** [November 25, 2022, 1:57am UTC](https://forum.elixirforum.com/t/ets-conditional-match-with-variable-in-elixir/52010/4 "2022-11-25T01:57:23Z")

</div>

> [@LostKobrakai](#):
>
> I’m not sure why exactly, but `:ets.fun2ms` doesn’t work in elixir

Hmmm, the full stack trace is interesting:

```elixir
iex(7)> :ets.fun2ms(fn {x,y,z,:_} when x == id or y == id -> {x,y,z} end)   
** (FunctionClauseError) no function clause matching in :ms_transform.normalise/1    
    
    The following arguments were given to :ms_transform.normalise/1:
    
        # 1
        {:EXIT,
         {:badarg,
          [
            {:lists, :keysearch, [:_@0, 1, %{_@0: 4}],
             [error_info: %{module: :erl_stdlib_errors}]},
            {:ms_transform, :fixup_environment, 2,
             [file: 'ms_transform.erl', line: 1104]},
            {:lists, :map_1, 2, [file: 'lists.erl', line: 1320]},
            {:lists, :map, 2, [file: 'lists.erl', line: 1315]},
            {:lists, :map_1, 2, [file: 'lists.erl', line: 1320]},
            {:lists, :map_1, 2, [file: 'lists.erl', line: 1320]},
            {:lists, :map, 2, [file: 'lists.erl', line: 1315]},
            {:ms_transform, :fixup_environment, 2,
             [file: 'ms_transform.erl', line: 1112]}
          ]}}
    
    (stdlib 4.1.1) ms_transform.erl:1124: :ms_transform.normalise/1
    (stdlib 4.1.1) ms_transform.erl:222: :ms_transform.transform_from_shell/3
    (stdlib 4.1.1) ets.erl:605: :ets.fun2ms/1
    iex:7: (file)

```

There’s actually TWO crashes happening here:

- `:ms_transform.normalize` is being called with an `{:EXIT, ...etc}` tuple because the error handling in `transform_from_shell` wasn’t expecting that shape:

[https://github.com/erlang/otp/blob/d95d79d1e363879462d9775316896c02b59771ee/lib/stdlib/src/ms\_transform.erl#L217-L225](https://github.com/erlang/otp/blob/d95d79d1e363879462d9775316896c02b59771ee/lib/stdlib/src/ms_transform.erl#L217-L225)

- that tuple is coming from `:ms_transform.fixup_environment`, which expects an environment `B` as a keyword list but gets a _map_ instead:

[https://github.com/erlang/otp/blob/d95d79d1e363879462d9775316896c02b59771ee/lib/stdlib/src/ms\_transform.erl#L1103-L1109](https://github.com/erlang/otp/blob/d95d79d1e363879462d9775316896c02b59771ee/lib/stdlib/src/ms_transform.erl#L1103-L1109)

The root cause is that `:erl_eval.fun_data/1` is returning a map for the bound variables 🤔 :

```elixir
iex(9)> :erl_eval.fun_data(fn {x,y,z,:_} when x == id or y == id -> {x,y,z} end)
{:fun_data, %{_@0: 4},
 [
   {:clause, 9,
    [
      {:tuple, 9,
       [{:var, 9, :_x@1}, {:var, 9, :_y@1}, {:var, 9, :_z@1}, {:atom, 9, :_}]}
    ],
    [
      [
        {:op, 9, :orelse, {:op, 9, :==, {:var, 9, :_x@1}, {:var, 9, :_@0}},
         {:op, 9, :==, {:var, 9, :_y@1}, {:var, 9, :_@0}}}
      ]
    ], [{:tuple, 9, [{:var, 9, :_x@1}, {:var, 9, :_y@1}, {:var, 9, :_z@1}]}]}
 ]}

```

---

<div class="post-metadata">

**Author:** ![krstfk](https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/k/c67d28/32.png) [@krstfk](https://forum.elixirforum.com/u/krstfk)\
**Post date:** [November 25, 2022, 8:28am UTC](https://forum.elixirforum.com/t/ets-conditional-match-with-variable-in-elixir/52010/5 "2022-11-25T08:28:26Z")

</div>

`fun2ms` and friends are parse transformations, they can be used from the shell, but the transformation is applied at compile time using `ms_transform.hrl` which cannot really be done in elixir. See [the full documentation](https://www.erlang.org/doc/man/ms_transform.html) especially the warnings at the end of the documentation.
