While writing a function, I am using argument matching like so:
def process_thing( %{} = thing )
I'm expecting that thing
is a Map, and is Enumerable. Unfortunately, this argument list is also matching Regexes specified as ~r/regex/
, and the Regex (although it returns true for is_map(~r/thing/)
) is not Enumerable.
How can I craft this function definition so that only Maps - or ideally, Enumerable things - are dispatched to this function?
There's no way to match on something being an Enumerable
. If you're ok with just maps, then you have the is_map/1
built-in function:
def process_thing(thing) when is_map(thing) do
...
end
One alternative is checking for all the data types that you expect and support:
def process_thing(thing) when is_map(thing) or is_list(thing), do: ...
def process_thing(%MapSet{}), do: ...
...
If you need to support all enumerables (maybe it will be easier to give good advice with more information on your use case), you can always use Protocol.assert_impl!/2
:
def process_thing(thing) when is_map(thing) or is_list(thing), do: ...
def process_thing(%{__struct__: struct}) do
assert_impl!(Enumerable, struct)
end
and handle the possible failure of Protocol.assert_impl!/2
. I'm not sure if this implementation is bullet-proof and again, there may be a cleaner way to implement this. :)
One more thing: if you want to match on maps but not match on structs (like Regex
), one way to solve it is to first match on the things you don't want to match so that you get them out of the way (and handle them however you need to):
def process_thing(%{__struct__: _}), do: # bad things here, we don't like structs
def process_thing(%{} = thing), do: # hey, that's a map now!