rhombus-prototype icon indicating copy to clipboard operation
rhombus-prototype copied to clipboard

Feature request: Map binding default value for absent key

Open AlexKnauth opened this issue 1 year ago • 2 comments

Extend the Map binding to allow specifying a default value to match when the key is not in the Map.

Example:

let { "x": x = #false }: { "x": 0 }
//x ~is 0
let { "y": y = #false }: { "x": 0 }
//y ~is #false

AlexKnauth avatar Aug 23 '24 02:08 AlexKnauth

edit: Ah, it seems that I have misunderstood the functionality. This is only for specific keys so it is not unbounded. You can ignore the bits below.

Wouldn't this effectively create a type of map that contains every possible key? This produces inconsistent behavior because List.find([key, ...], fun(x): x == "key") -> #false but my_map["key"] -> 0 or any other non #false value that was provided as the default. One option might be to have such maps have a different type (subtype?) but that seems fraught to me. Another solution that would maintain consistent behavior would be to allow my_map["key" ~default: = #false] at lookup time so that instead of raising a key error the specified value would be returned at the call site (maybe it already does this?).

Forcing a default value can create a nasty footgun where it becomes impossible to distinguish between a value that came from a key that is actually in the map vs a key not in the map. It is still possible to check explicitly for the key, but if there is code that handles maps in general and gets passed one of these special maps there are many cases where the expectation about the behavior of missing keys can lead to bugs. In particular I've done this to myself in python when the builtin python dict and defaultdict are mixed together, and it is bad enough that I now defensively program to prevent any possible mixing of the two types.

tgbugs avatar Aug 23 '24 17:08 tgbugs

For those who are new to Racket/Rhombus: map[key] and map.get(key) desugar to (hash-ref map key), which throws if the key isn’t in the map. To supply a default value, you can use map.get(key, default_val), which desugars to (hash-ref map key default_val). We don’t force a default value, because a map can in principle contain any value (even the special unsafe-undefined value, although that can break code that treats that as “not found”).

The issue is about extending the binding (pattern) form for matching a map with optional keys.

usaoc avatar Aug 24 '24 02:08 usaoc