tweag / nickel

Better configuration for less
https://nickel-lang.org/
MIT License
2.23k stars 85 forks source link

Wildcard patterns #1904

Closed yannham closed 2 months ago

yannham commented 2 months ago

This PR adds a special pattern that match any value but doesn't bind anything, the wildcard pattern _.

This subsumes the previously special case of the catch-all case of match expression _ => default_value. Now a match expression is simply a list of patterns, and _ is just one of them.

This addition allows to use _ anywhere in a nested pattern, like 'Foo ('Bar _), which is especially useful when matching on enum variants. The ideas introduced for the typechecking of the catch-all case _ => of match expressions naturally extend to wildcards pattern: if somewhere in a nested pattern, there is a wildcard which is in the same position as an enum variant in a sibling pattern, like match { {foo = 'Bar} => something, {foo = _} => something_else}, this makes the corresponding enum type open: the above match expression has type forall r. {foo : [| 'Bar; r |]} -> SomeType.

A wildcard pattern doesn't force the value being matched on, following the idea that it's equivalent to let _x = value in body (but without binding _x). In general, from now on, we'll try to follow the idea that the semantics of destructuring let <pattern> = value in body should be equivalent to value |> match { <pattern> => body, _ => <NonExhaustiveDestructuringError> }. Following this guideline, it confirms that let _ shouldn't force the matched value. One last argument for this behavior is that fun _ => body is desugared to fun fresh_var => let _ = fresh_var in body, which means that making let _ strict would make fun _ => body strict in its argument, which is arguably very surprising (for reviewers, this contradicts what I said today earlier in the weekly meeting, because I made a mistake with wildcard patterns specifically - the "destructuring should force" is still valid for other patterns though, and it agrees with everything said here)