tweag / nickel

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

Allow some function equality comparison #1978

Closed yannham closed 2 days ago

yannham commented 1 week ago

Relax the initial restriction on comparing functions, introduced in #975, which is that comparing a function to anything else errors out. In practice comparing a function to a value of some other type isn't a problem, and can be useful to allow patterns like fun x => if x != null then ... else ... without having first to defensively check if x is a function, because that would fail at runtime. One can think for example of quick contracts such as let IsZero = from_predicate ((==) 0), which currently fails with a dynamic type error if we try to do (fun x => x) | IsZero. We'd need to do something like from_predicate (fun x => !(std.is_function x) && x == 0), which is annoying.

Instead, we only forbid comparison between two function-like values (functions, match expressions and custom contracts) and between two opaque foreign values. Comparing a function-like value to a number of a string is fine and just returns false.

The initial motivation for restricting function comparison is that it's never something that is meaningful to do, so it's most probably an error, and that it breaks reflexivity of ==, because f != f when f is a function. Arguably, the relaxed restriction proposed in this PR still fits the bill.

For context, this came up in #1975, where custom contracts is internally a pair of function (immediate, delayed), each of which can be null, but then we can't do control flow such as if immediate != null then .... While the %typeof% work-around does work, it doesn't feel right.

yannham commented 2 days ago

Maybe there could also be a test that comparing a function to a number doesn't fail?

Fair! Added in the latest commits.