digitalmars.D - What is the rationale behind enabling Nullable!T to be used as a
- Adnan (18/18) Feb 14 2020 Nullable!T, as I understand is somewhat similar to Rust's
- FeepingCreature (12/20) Feb 14 2020 The rationale is we have a few more months to wait before the
- DanielG (3/3) Feb 14 2020 There's also the 'optional' package if you're looking for
- Adnan (11/14) Feb 14 2020 Not sure if this is any better
- Petar Kirov [ZombineDev] (5/20) Feb 14 2020 It's by design. optional presents a range interface of length 0
- FeepingCreature (8/23) Feb 14 2020 With std.typecons post deprecation:
- Petar Kirov [ZombineDev] (12/21) Feb 14 2020 The two ends of the design spectrum (as I see it) are:
- Petar Kirov [ZombineDev] (3/8) Feb 14 2020 Edit: `x.map!op`.
-
Adnan
(26/39)
Feb 14 2020
Very interesting. I always thought that Option
is a type-guard - Eugene Wissner (34/50) Feb 14 2020 It seems to be an attempt to implement a functor in idiomatic D.
- Paul Backus (5/24) Feb 14 2020 Aliak's package offers several features in addition to the range
- aliak (30/49) Feb 14 2020 Ya! If you take scala for e.g. Optional!T is an attempt to model
- Elronnd (3/6) Feb 14 2020 Not exactly, as others have mentioned. But there is
Nullable!T, as I understand is somewhat similar to Rust's Option<T>. It's meant to be an alternative to sentinel value based error checking. In regular Rust code if you do something like let x: Option<i32> = None; println!("{}", x + 4); It should throw compile error, since the `x` in `x + 4` is not checked for null. OpAdd<rhs = i32> is not implemented for Option<i32> for that reason. Of course, you can get the value using the unwrap() method, which would create runtime crash for null option. However when typing-out "unwrap()" you understand what you are signing up for. Back to D. const Nullable!int a; assert(a.isNull); writeln(a + 4); // compiles with 0 warnings Why is opBinary implemented for Nullable!T? Doesn't it defeat its purpose?
Feb 14 2020
On Friday, 14 February 2020 at 08:54:41 UTC, Adnan wrote:What is the rationale behind enabling Nullable!T to be used as a regular T?The rationale is we have a few more months to wait before the deprecation period for that functionality runs out and we can remove it. :)Back to D. const Nullable!int a; assert(a.isNull); writeln(a + 4); // compiles with 0 warnings Why is opBinary implemented for Nullable!T? Doesn't it defeat its purpose?If you switch to 2.090.1, you will get warnings there. If you run with -de, the warnings will be errors. --- The original reason was I believe that Nullable was never really intended to be Optional, it was intended to give types a "null" state like objects and pointers. Well, objects and pointers crash when you access them if they're null, so... (No, it's not a very good reason.)
Feb 14 2020
There's also the 'optional' package if you're looking for something more functional-program-ey: https://code.dlang.org/packages/optional
Feb 14 2020
On Friday, 14 February 2020 at 10:35:52 UTC, DanielG wrote:There's also the 'optional' package if you're looking for something more functional-program-ey: https://code.dlang.org/packages/optionalNot sure if this is any better /+dub.sdl: dependency "optional" version="~>1.0.0" +/ import optional; void main(const string[] args) { static import std; const auto n = no!int(); std.writeln(n + 4); // [] }
Feb 14 2020
On Friday, 14 February 2020 at 12:40:54 UTC, Adnan wrote:On Friday, 14 February 2020 at 10:35:52 UTC, DanielG wrote:It's by design. optional presents a range interface of length 0 or 1. See the docs for more info: https://github.com/aliak00/optional/#summaryThere's also the 'optional' package if you're looking for something more functional-program-ey: https://code.dlang.org/packages/optionalNot sure if this is any better /+dub.sdl: dependency "optional" version="~>1.0.0" +/ import optional; void main(const string[] args) { static import std; const auto n = no!int(); std.writeln(n + 4); // [] }
Feb 14 2020
On Friday, 14 February 2020 at 12:40:54 UTC, Adnan wrote:On Friday, 14 February 2020 at 10:35:52 UTC, DanielG wrote:With std.typecons post deprecation: import std.typecons; void main() { static import std; const auto n = Nullable!int(); std.writeln(n.apply!(i => i + 4)); // Nullable!int() }There's also the 'optional' package if you're looking for something more functional-program-ey: https://code.dlang.org/packages/optionalNot sure if this is any better /+dub.sdl: dependency "optional" version="~>1.0.0" +/ import optional; void main(const string[] args) { static import std; const auto n = no!int(); std.writeln(n + 4); // [] }
Feb 14 2020
On Friday, 14 February 2020 at 08:54:41 UTC, Adnan wrote:Nullable!T, as I understand is somewhat similar to Rust's Option<T>. It's meant to be an alternative to sentinel value based error checking. In regular Rust code if you do something like let x: Option<i32> = None; println!("{}", x + 4); It should throw compile error, since the `x` in `x + 4` is not checked for null. OpAdd<rhs = i32> is not implemented for Option<i32> for that reason.The two ends of the design spectrum (as I see it) are: 1. `Optional(T) x` as a range of [0,1] elements of type `T`. For all possible operations `op` on type `T` there exists an operation `Option(T).op` and the effect of executing is equivalent to `x.map!fun`. This is what aliak's optional package provides. 2. `Optional(T) x` behaves like a null-able pointer. The compiler statically prevents dereferencing if it can't prove that x is non-null. Such as scheme (although limited to just pointers for now) is work in progress: https://github.com/dlang/dmd/blob/master/changelog/ob.md
Feb 14 2020
On Friday, 14 February 2020 at 14:43:22 UTC, Petar Kirov [ZombineDev] wrote:The two ends of the design spectrum (as I see it) are: 1. `Optional(T) x` as a range of [0,1] elements of type `T`. For all possible operations `op` on type `T` there exists an operation `Option(T).op` and the effect of executing is equivalent to `x.map!fun`.Edit: `x.map!op`.
Feb 14 2020
On Friday, 14 February 2020 at 14:43:22 UTC, Petar Kirov [ZombineDev] wrote:On Friday, 14 February 2020 at 08:54:41 UTC, Adnan wrote: The two ends of the design spectrum (as I see it) are: 1. `Optional(T) x` as a range of [0,1] elements of type `T`. For all possible operations `op` on type `T` there exists an operation `Option(T).op` and the effect of executing is equivalent to `x.map!fun`. This is what aliak's optional package provides.Very interesting. I always thought that Option<T> is a type-guard for enforced null checks (I could be wrong here). But seems to me that this design circles back to square 1: having the callee remember to check if the length of the range is 0 or 1. Which is essentially similar to check sentinel values (i.e. check if the binarysearch returns -1 as index). What languages do this? What does Aliak's package provide that's fundamentally different to just returning a T[]? Empty T[] would mean `None` and a T[] with 1 item means `Some(T)`?2. `Optional(T) x` behaves like a null-able pointer. The compiler statically prevents dereferencing if it can't prove that x is non-null. Such as schemeNim's optional works this way too: import options let v = none(int) Scala: val a: Option[Int] = None println(a + 4) // compile error Even C++: #include <iostream> #include <optional> auto main() -> int { std::optional<int> a; std::cout << a + 4 << std::endl; // compile error }(although limited to just pointers for now) is work in progress: https://github.com/dlang/dmd/blob/master/changelog/ob.mdNice, any chance it's going to work with non-ptrs too?
Feb 14 2020
On Friday, 14 February 2020 at 17:04:28 UTC, Adnan wrote:On Friday, 14 February 2020 at 14:43:22 UTC, Petar Kirov [ZombineDev] wrote:It seems to be an attempt to implement a functor in idiomatic D. In Haskell you have lists and you can map over lists. For example: fmap (+1) [1, 2, 3] will add 1 to each element of the list [1, 2, 3]. But you can map over many other different types, such as Maybe fmap (+1) Nothing will return Nothing. fmap (+1) (Just 1) will return (Just 2). You can map over Eithers, e.g. for either string or integer: fmap (+1) (Left "some type") will return 'Left "some type"'. fmap (+1) (Right 5) will return 'Right 6'. Haskell has even an "empty" function which returns "Nothing" for Maybe, or an empty list for lists. But maybes have nothing to do with lists in Haskell. Functors allow to write concise, type safe code without all that "if-noise" if (a.isNothing) { do some stuff } else { I don't care } Functors is a very generic concept, appliable to many data types and there is a bunch of functions that work on functors. Since in D all kinds of algorithms are implemented for ranges, Optional seems to try to use it and say: Well let us imagine we have a range which can be empty or contain at most one element. It is pretty the same as Maybe/Option/Optional, but many algorithms that work on ranges (such as map) can be applied to this type. Not sure if it is an answer, but I think it is the actual background.On Friday, 14 February 2020 at 08:54:41 UTC, Adnan wrote: The two ends of the design spectrum (as I see it) are: 1. `Optional(T) x` as a range of [0,1] elements of type `T`. For all possible operations `op` on type `T` there exists an operation `Option(T).op` and the effect of executing is equivalent to `x.map!fun`. This is what aliak's optional package provides.Very interesting. I always thought that Option<T> is a type-guard for enforced null checks (I could be wrong here). But seems to me that this design circles back to square 1: having the callee remember to check if the length of the range is 0 or 1. Which is essentially similar to check sentinel values (i.e. check if the binarysearch returns -1 as index). What languages do this?
Feb 14 2020
On Friday, 14 February 2020 at 17:04:28 UTC, Adnan wrote:On Friday, 14 February 2020 at 14:43:22 UTC, Petar Kirov [ZombineDev] wrote:Aliak's package offers several features in addition to the range interface that make it more convenient to work with than a simple array. You can see a list in the README: https://github.com/aliak00/optional/blob/v1.0.0/README.md#featuresOn Friday, 14 February 2020 at 08:54:41 UTC, Adnan wrote: The two ends of the design spectrum (as I see it) are: 1. `Optional(T) x` as a range of [0,1] elements of type `T`. For all possible operations `op` on type `T` there exists an operation `Option(T).op` and the effect of executing is equivalent to `x.map!fun`. This is what aliak's optional package provides.Very interesting. I always thought that Option<T> is a type-guard for enforced null checks (I could be wrong here). But seems to me that this design circles back to square 1: having the callee remember to check if the length of the range is 0 or 1. Which is essentially similar to check sentinel values (i.e. check if the binarysearch returns -1 as index). What languages do this? What does Aliak's package provide that's fundamentally different to just returning a T[]? Empty T[] would mean `None` and a T[] with 1 item means `Some(T)`?
Feb 14 2020
On Friday, 14 February 2020 at 17:04:28 UTC, Adnan wrote:On Friday, 14 February 2020 at 14:43:22 UTC, Petar Kirov [ZombineDev] wrote:Ya! If you take scala for e.g. Optional!T is an attempt to model a lot of what scala does. I.e. it's a range. If you want to get to the element you have to check if it exists first, else you manipulate it via functional primitives like map. You need to do the same in scala, kotlin, swift for e.g. I used to have an unwrap function in there as well, and you could do: if (auto v = optional.unwrap) { // you get a pointer to T here and in D you can access members of a pointer // just like it wasn't a pointer. } But that proved to be hard to keep safe. I've never been a 100% on the whole operator overloading though. The difference between how it's implemented here and other places is that it's a noop if the optional is empty. In swift you can call functions on optionals even if they're empty, so that's what the oc utility in optional is for, and the opBinary was a way to add that same functionality for D operators and array access. Basically, operating on an optional is ok and always results in an optional. And it must always maintain it's emptiness if any of the operands are empty.On Friday, 14 February 2020 at 08:54:41 UTC, Adnan wrote: The two ends of the design spectrum (as I see it) are: 1. `Optional(T) x` as a range of [0,1] elements of type `T`. For all possible operations `op` on type `T` there exists an operation `Option(T).op` and the effect of executing is equivalent to `x.map!fun`. This is what aliak's optional package provides.Very interesting. I always thought that Option<T> is a type-guard for enforced null checks (I could be wrong here). But seems to me that this design circles back to square 1: having the callee remember to check if the length of the range is 0 or 1. Which is essentially similar to check sentinel values (i.e. check if the binarysearch returns -1 as index). What languages do this?What does Aliak's package provide that's fundamentally different to just returning a T[]? Empty T[] would mean `None` and a T[] with 1 item means `Some(T)`?I've tried to outline as much as possible in the readme. In a nutshell though: intent, nogc, safe access, no need to worry about having more than one element - I may be forgetting some things. Cheers, - ali
Feb 14 2020
On Friday, 14 February 2020 at 08:54:41 UTC, Adnan wrote:Nullable!T, as I understand is somewhat similar to Rust's Option<T>. It's meant to be an alternative to sentinel value based error checking.Not exactly, as others have mentioned. But there is https://github.com/moon-chilled/tenbots-opensource/blob/master/maybe.d (which originally comes from https://github.com/dkhasel/maybe-d, but I cleaned it up a bit). At one point, I wrote a wrapper which would let you write a function with a return type of Maybe!T, but which actually returned objects of type T. But that was a giant hack and not at all worth it.
Feb 14 2020