Bug in Elixir? Map update in guards

Why map update syntax does not work in guards?

1> case [1, 2, 3] of
1>   X when #{x => 1}#{x => 2} =:= #{x => 2} -> X
1> end.
[1,2,3]

and

iex> case [1, 2, 3] do
...>   x when %{%{x: 1} | x: 2} == %{x: 2} -> x
...> end

Should I submit it as a bug, or is it intended?

2 Likes

I don’t think that is supported, there’s still a few things in guards that haven’t made it over in the parser, for example I believe map_get is not supported.

map_get is supported because it is a function, not an expression

iex(1)> case %{x: 1} do
...(1)>   x when :erlang.map_get(:x, x) == 1 -> x
...(1)> end
%{x: 1}

IIRC Erlang supports only updates of literal values and I am not sure there is a need for it in Elixir because you could merge them at compile time if you need similar behavior.

3 Likes

What would be the use case for this? Isn’t the above guard checking whether 2 == 2? :thinking:

2 Likes

What do you mean by literal values?

1> X = x.
2> M = #{x => 1}.
3> case 1 of
3>   Y when M#{X => Y} =:= #{x => 1} -> ok
3> end.
ok
1 Like

TIL! That’s news to me. :slight_smile:

It is something we could support then. Is there any use case for it though?

1 Like

I thought about something like

defguard is_same_struct?(left, right) when %{left | meta: nil} == %{right | meta: nil}

Or

defguard equal_request?(left, right) when %{left | timestamp: nil} == %{right | timestamp: nil}
4 Likes

Or even more general and useful example

defguard field_polymorphic?(left_struct, right_struct) when %{left_struct | __struct__: nil} == %{right_struct | __struct__: nil}
1 Like

Hrm… good question. I wonder if that’s indeed the fastest way to compare maps in such scenarios?

How would you compare them the other way? Key-by-key using :maps.iterator?

For a small, known map, I would get the values directly and compare them.

1 Like

This works only if you know all the keys by name.

1 Like