Opaque Contract type
It used to be that contracts were all functions (morally) of type Label -> Dyn -> Dyn
Now we also allow special record contracts, and we are discouraged from actually using the contracts as functions. (they should be used with std.contract.apply instead)
It would reduce confusion and mistakes, and allow for better error reporting and more features to have an opaque Contract type, and require calling some std.contract.from_function to convert from a function that used to just implicitly be a Contract.
Is your feature request related to a problem? Please describe.
- #1460
- People not using
std.contract.apply - Can add extra information (e.g. "came from
std.contract.from_predicate" means we can freely||or&&the contracts) - Can require that any term in contract position (i.e. after
|) must be of type Contract
Describe the solution you'd like
An opaque Contract type, with explicit conversion from a function that would right now just be a contract
Describe alternatives you've considered
The way we do it now works, though with the problems mentioned above. We could also add an opaque contract type, but have automatic conversion from functions Label -> Dyn -> Dyn when needed.
Additional context This would be a breaking change, so it could only happen in Nickel 2.0
@yannham I don't see a tag that signifies "breaking change"
This was discussed in the weekly again, because of some reports similar to https://github.com/tweag/nickel/issues/1460 (on a very large codebase). A possible backward-compatible solution would be to add this new contract opaque type, but still allow implicit conversion from a function to the corresponding contract. Now, this would negate some of the benefits, as we can't complain when something in contract position is not a contract, and so we can't really solve #1460 directly.
However, we can issue a deprecation warning in those cases, and at least the users now have the possibility to move to the new way (the modification is likely to just add a call to a hypothetical std.contract.from_function here and there, so nothing major).
This is more or less what's been implemented in #1987. This only concerns functions, but functions are the problematic bit (for the rest, records and static types can't be applied, and they are all valid contracts). We went the backward-compatibility route and still accept naked functions, but document this as legacy and will be deprecated.