Hello,
I have come across a weird (to me) situation.
When I receive response from DB I want to pattern match it like so:
response = {:ok, [%SomeStruct{hidden: true}]}
case response do
{:ok, result} when result in [[], [%{hidden: true}]] ->
:do_something
_ ->
:do_something_else
end
Basically I’m trying to route code execution to first branch if the result is either [] or has one item, struct, that has key :hidden set to true (e.g. [%{hidden: true}]).
But the match does not succeeds… Of course I can write it out without the use of when result in ... (resulting in 2 branches with identical code) but I would like to know why is this happening…
Avoid duplicating the branch though. You can do something like that:
response = {:ok, %{hidden: true, other: "field"}}
case response do
{:ok, result} when result == [] when :erlang.map_get(:hidden, result) == true ->
:do_something
_ ->
:do_something_else
end
Edit: reading the first and last posts of this topic I would believe Elixir had a map_get function but it does not seem so.
So you are saying that before the proposal we could not use a property in a matched map in defguard? Because I believe def some_func(%{hidden: value}) when value == true would work in early Elixir versions already.
Wow. Didn’t know that I can create own guards… that’s pretty neat… It will reduce the function heads’ sizes significantly.
Just a clarification: The result is always an array. Either it empty or it has single map with hidden: bool.
So is there any function callable from guard that can extract enum’s item at index? I don’t know if Enum.at/1 is callable from guard.
Something like {:ok, res} when res == [] or is_hidden(Enum.at(res, 0)) ->.
(I’m not with the PC right now, so cannot try it…)
You can use hd/1/tl/1 tango if it is a list. In general sense it is not possible. And you always could define your own guards, just in past it was more troublesome as you needed to write whole macro on your own.
For example this:
defguard is_hidden(%{hidden: value}) when value == true
Would need to be written as:
defmacro is_hidden(map) do
quote do
:erlang.map_get(:hidden, map) == true
end
end