-
Notifications
You must be signed in to change notification settings - Fork 98
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add std.cast #2184
base: master
Are you sure you want to change the base?
Add std.cast #2184
Conversation
|
Branch | cast |
Testbed | ubuntu-latest |
Click to view all benchmark results
Benchmark | Latency | microseconds (µs) |
---|---|---|
fibonacci 10 | 📈 view plot 🚷 view threshold | 392.24 µs |
foldl arrays 50 | 📈 view plot 🚷 view threshold | 1,654.30 µs |
foldl arrays 500 | 📈 view plot 🚷 view threshold | 5,771.80 µs |
foldr strings 50 | 📈 view plot 🚷 view threshold | 6,671.40 µs |
foldr strings 500 | 📈 view plot 🚷 view threshold | 58,862.00 µs |
generate normal 250 | 📈 view plot 🚷 view threshold | 41,280.00 µs |
generate normal 50 | 📈 view plot 🚷 view threshold | 1,792.70 µs |
generate normal unchecked 1000 | 📈 view plot 🚷 view threshold | 2,796.40 µs |
generate normal unchecked 200 | 📈 view plot 🚷 view threshold | 657.31 µs |
pidigits 100 | 📈 view plot 🚷 view threshold | 2,769.70 µs |
pipe normal 20 | 📈 view plot 🚷 view threshold | 1,312.80 µs |
pipe normal 200 | 📈 view plot 🚷 view threshold | 8,769.50 µs |
product 30 | 📈 view plot 🚷 view threshold | 761.82 µs |
scalar 10 | 📈 view plot 🚷 view threshold | 1,321.70 µs |
sum 30 | 📈 view plot 🚷 view threshold | 752.75 µs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can see the motivation for such an operation. As preliminary questions:
- What is the motivation to make this a primop? Is it to avoid undue contract evaluation? Because otherwise I guess you could implement it in user-code as
x |> %typeof% |> match { 'Number => 'Number (x | Number), ...etc}
. - I think it's a nice solution with the current features of Nickel. In the long term, though, I wonder if we won't want to implement some form of occurrence typing (or flow-sensitive typing), which would make the original example work without contract annotation (à la TypeScript). In this case I'm wondering if adding a different way of doing that now is a good idea on the long-term. On the other hand, it's filling a need, and it's not awful if at some point we have
cast
andtypeof
overlap; we can deprecatecast
, which is still quite readable/understandable.
So I guess there are three possible solutions:
- This PR
- This PR but without the primop
- Move
cast
to an external lib for the time being (or let people implement it if they need it)
I guess there isn't good evidence that this is needed in the standard library. The main reason I went for the primop was to avoid the contract application for possible (but unmeasured) performance reasons. Regarding occurrence/flow-sensitive typing, I'm probably biased by using rust as my main language for many years now, but what benefit does it have over ADTs + pattern matching? |
flow-sensitive typing makes sense for gradually typed language, precisely for this use-case: you get some untyped data in a typed context, but thanks to some blessed test, the typechecker can refine the type of the values in each branch. TypeScript uses this to emulate ADTs, but I think it's useful in general. Since this kind of decision tree This technique doesn't really make sense as it is for strongly, statically, nominally-typed language like Rust, Haskell, OCaml, where you can't really "cast" or refine values to begin with. Although you could argue that refinement types, dependent types and GADTs do require vaguely similar mechanisms. However, I also realize that all the simple examples I can think of could probably be covered by the "produce an ADT" approach as well. So maybe |
Sure, I can also just open an issue and we can let it sit for a while. I don't think there's any urgency for this |
This adds a
std.cast
function that's like the payload-carrying version ofstd.typeof
. The motivation is to make it easier to write statically typed code that checks dynamic types.The immediate motivation was for the contracts in json-schema-to-nickel, where statically typing the
max_properties
contract looks likeWith this addition, you can write it as