It’s confusing because maps are the only thing that can do such an inexplicit partial match. Be careful when doing say assert %{} =. On the other hand you can imagine why it’s more useful to do matching on map partials than on fully specified maps; and in general erlangs/elixir choices are largely driven by utilitarianism more than consistency.
Also because if %{} matched exactly empty maps then you would need to use a guard to match on any non empty map when not specifying keys (is_map) and those were only added later. Also, I would usually say that we pattern match much more commonly on the map form with or without any keys than on a map being empty (as such it’s much more useful as you mention), as that is probably a sign that those functions are not using the appropriate data structure if they care about a map being empty.
Matching maps could have been done similar to how nix deals with it in attribute sets.
A key specified requires it to be present.
A key not specified disallows it do be present, unless the special key ... is given, which allows for arbitrary additional keys beeing present.
Transfered to elixir this would mean:
%{} matches the empty map, %{...} matches any map (including the empty) %{foo: _} matches the map that has only the :foo key, while %{foo: _, ...} would match any map that has at least the :foo key.
The problem: Matching #{} as empty map is not even allowed on the BEAM. So elixir can not support a syntax as expressed above, unless erlang/OTP introduces a way of matching the empty map without guards.
Yeah, the empty map could see a lot of use in tests at least (and some regular code too occasionally), the others I think they are more or less covered by just having structs - I also don’t think it’s a good option to allow bare maps in a program drive flow according to if they have a key or not - if something depends on keys being present it makes more sense to me to cast that to a proper struct/ecto/record than having its meaning codified by fields being present or missing from it. It would also be a breaking change to introduce the exact keys match syntax - although if it was there from the beginning it would probably see a lot of usage but personally I only “miss” the empty map match.
just to clarify Matching #{} as empty map is not even allowed on the BEAM doesn’t mean you can’t match empty map. You can use a with clause to achieve this:
match?(m when m == %{}, value)
for example works just fine, as it does inside case clauses, or in function guards. You can also assign to a variable and then use the pin operator.
Possibly surprisingly, using an @ attribute does not work by “subbing as a variable and pinning” first, which I think is one of the few footguns in elixir.
A guard clause you mean, but guards are not pattern matches.
A pattern match can be reordered within limits. Binding to a name and then checking conditions in a guard can’t. Even worse, they make it impossible for the BEAM to reorder matches on each side of the clause with the guard