5 ms·
I agree. Add null checking at compile time with something like checkerframework to get the best of both worlds.
by plmpsu 5y ago
I agree. Add null checking at compile time with something like checkerframework to get the best of both worlds.
- brabel 5y agoExactly. I don't get why people complain about null-pointers in Java when all it takes to get rid of them is enabling static analysis that enforces checking for null at compile time... as that's exactly as safe as using Optional, and as Kotlin has shown, it's also possible to have a nicer interface (e.g. `something?.let { it.isNotNullHere() }`) with bare nulls... without incurring the cost of allocation or of declaring generic types everywhere. Since I started using "safe" nulls with Ceylon, nearly a decade ago, later with Kotlin and TypeScript, I've been firmly in the camp of supporting nulls in the language instead of `Optional` or `Maybe`. Rust should've used them IMO... using `?` for error propagation instead is such a weird choice.
- Zababa 5y ago? is used for Result, and not for Option. For Option, you use .unwrap(). Supporting nulls in the language also means that everything else is going to be second-class, while sum types allows you to do pretty much everything you want.
- kangalioo 5y ago(? works for both Option and Result, as does unwrap(). ? propagates to the caller, while unwrap() initiates a panic and unwinds the stack)
- khuey 5y ago? is used for Option. Just like Result, ? propagates the Err/None to your caller, and unwrap asserts that it's not present.
- deleted 5y ago[deleted]
- brabel 5y agoLooks like you have much to learn about Rust. Option can also use `?` Example (from [1]): // Assume // fn halves_if_even(i: i32) -> Option<i32> fn do_the_thing(i: i32) -> Option<i32> { let i = halves_if_even(i)?; // use `i` } Besides, the `?` is syntax sugar for the `try!` macro as explained in the `try!` macro docs [2], and has nothing to do with sum types, at least not directly. Ceylon would be a closer case where nullable types are represented as sum types and not a special-case in the language (`String | Null` is the same as `String?`)... but Ceylon, as Rust, special-cased the `?` operator for "something"... in Ceylon, they are used for null-checks, and in Rust, for propagating errors via the `try!` macro, which is what my previous comment was about. [1] https://stackoverflow.com/questions/42917566/what-is-this-question-mark-operator-about https://stackoverflow.com/questions/42917566/what-is-this-qu... [2] https://doc.rust-lang.org/std/macro.try.html https://doc.rust-lang.org/std/macro.try.html
- Zababa 5y agoYou're right, I'm not sure how I thought that it worked only with Result. Thanks for the correction. > Ceylon would be a closer case where nullable types are represented as sum types and not a special-case in the language (`String | Null` is the same as `String?`). Aren't those union types? My understanding is that they are different from sum types, as you usually have to declare the sum types beforehand, and sum types are collections of values, while union types are collections of types, and are often declared "on the spot". At least that's how they work in Typescript, I don't know much about Ceylon. To go back to option types, people usually like them because there are well-known patterns to program with them (like map). Of course you can get the same safety with static flow analysis (Typescript does this), but I think part of it is ergonomics, influenced by functional programming. Though if there is an overhead like in Java, I'm not sure about their value.
- brabel 5y agoI don't know if there's a meaningful difference between sum types and union types as both end up allowing a variable to be of one of a set of types. Ceylon: alias StringOrInt = String | Integer; StringOrInt myValue = "foo"; StringOrInt otherValue = 10; Rust: enum StringOrInt { Str(String), Int(usize), } let my_value: StringOrInt = StringOrInt::Str("foo"); let other_value: StringOrInt = StringOrInt::Int(10); In Ceylon, you're right you don't need to name a "union", which IMO makes it much nicer, as I was able to use adhoc unions everywhere, e.g. inside streaming operations: [ for x in foos if (x.something()) "foo" else 10 ] The above "automatically" has type `[String | Integer]`. In Rust (and Kotlin) I would need to name a type just for this. Another disadvantage of Rust's approach is that the type variants are not types themselves. That's really annoying sometimes, e.g. you want to have a method that only handles one of the variants, but you can't (you need to pass the values the variant wraps separately, but that may be undesirable semantically as that would allow something that has no instance of the enum to also use the function).
- pkolaczk 5y agoWith Kotlin based approach to nulls you can't represent Option<Option<T>> values.
- brabel 5y agoAnd that's a good thing, no? I've never met a good use cases for double-wrapping values like this.
- Zababa 5y agoI'm not an expert on Kotlin but this reminds me of JS promises. The issue was that by not supporting Promise<Promise<T>>, promises were not monadic. It look like the same issue in Kotlin.
- nemetroid 5y agoI thought this was a good example: https://news.ycombinator.com/item?id=28746293 https://news.ycombinator.com/item?id=28746293
- pjmlp 5y agoBecause otherwise how would they sell their favourite guest language? It is like Google uses their pseudo Java dialect when selling Kotlin features, instead of comparing it with proper Java.