racket / rhombus

Rhombus programming language
Other
332 stars 59 forks source link

support "default" values in `Map` bindings #551

Closed usaoc closed 1 week ago

usaoc commented 1 week ago

In a Map binding, a key can be made optional by supplying a default expression/body, which is used to produce a "default" value to further match in case the key is missing. This is similar in functionality to the 2-ary case of Map.get.

Resolve #547.

mflatt commented 1 week ago

This looks great! In trying it out, I notice one issue:

> def {"1": a, "2": b = 2} = {}
def: value does not satisfy annotation
  value: {}
  annotation: matching(Map{"1": _, "2": _})

The constructed annotation shown in the error message suggests that a "2" is needed. Should keys with defaults be left out there?

A danger of just leaving them out is that an error like this (simulated) one might be confusing:

> def {"1": a, "2": b :: Int = "oops"} = {"1": 1}
def: value does not satisfy annotation
  value: {"1": 1}
  annotation: matching(Map{"1": _})

I think this matters less than the first example above, though.

usaoc commented 1 week ago

Maybe we can add = .... to the right of value binding to indicate that a default value is supplied. It can still be confusing when the produced default value doesn’t match the value binding, though. This feature is unusual in that the “default” value is specified in the Map binding, and I’m not sure how to best phrase the error.

mflatt commented 1 week ago

I think you're right about = ..... My initial thought was that the consuming side wouldn't want to expose the use of that key, but now I think that doesn't make sense. And it addresses the second example by suggesting that the issue may be within the .....

usaoc commented 1 week ago

Now a = .... (in this form, regardless of whether an expression or block is supplied) is added to the right of value binding to indicate that a default value is supplied.

mflatt commented 1 week ago

Thanks! LGTM