Something like a Map with patterns as keys

I am brainstorming how to create a data-structure that contains anonymous functions that are used if a value matches a pattern. The data-structure needs to change dynamically.
A map seems to fit:

%{{1,2,3} => :func_a,{2,3,4} => :func_b}[{1,2,3}]

But the key is not a pattern it would be great to be able to do so.

I was hoping that it could be specified like a case statement:

case {1, 2, 3} do
   {4, 5, 6} ->
     "This clause won't match"
   {1, 2, x} ->
     "This clause will match and bind x to 3 in this clause"
   _ ->
     "This clause would match any value"
end

like this:

%{{1,2,_} => :func_a,{2,3,_} => :func_b}[{1,2,3}]

but this is not possible.
any suggestions?

Not sure I get what you want but storing patterns like {1, 2, x} is not possible in Elixir.

Can you demonstrate one complete case of what would a successful implementation achieve and do?

2 Likes

Maps are hash tables (for 32+ elements) indexed by key. Therefore a lookup such as {1, 2, _}, where we don’t fully have to key to hash, would require a linear traversal of all keys. In other words, maps are really not a good fit for the lookups you want to perform, regardless of Elixir having syntax for it or not.

Luckily, a relatively straight-forward solution is to nest your keys:

map = %{{1,2} => %{3 => :func_a},{2,3} => %{4 => :func_b}}

This way, if you want to lookup 1, 2, 3, you do:

map[{1, 2}][3]

if you want any (or all) keys starting with {1, 2}, then you do:

map[{1, 2}]
2 Likes

Something which might be interesting for you could be :ets.fun2ms which transforms a function to an ets match specification.

2 Likes

Well, partially you can by using match spec and then using :erlang.match_spec_test([value], match_spec, :table).

1 Like

ok this would look like that right:

a = fn({a,b,c}) when a==1 and b==2 -> c end
b = fn({a,b,c}) when a==2 and b==3 -> c end
table = :ets.new(:table, [:set, :protected])
ms = :ets.fun2ms(a)++:ets.fun2ms(b)
:ets.insert(table, { :ets.fun2ms(a), a})
:ets.insert(table, { :ets.fun2ms(b), b})
:erlang.match_spec_test({1,2,3}, ms, :table) # ok 
:erlang.match_spec_test({2,3,10}, ms, :table) # ok
:erlang.match_spec_test({1,3,2}, ms, :table) # doesn't match

What irritates me about this solution is (1) that I have to two things to take care of, the array of match_specs and the table. And (2) I have to use a atom to refer to the table.

Am I right about this? or do use these functions in a unintended way?