digitalmars.D - Nullable or Optional? Or something else?
- Andrei Alexandrescu (10/10) Sep 02 2009 I plan to add a Nullable struct to Phobos (akin to C#'s Nullable,
- Danny Wilson (4/14) Sep 02 2009 How about: Maybe!T
- Simen Kjaeraas (4/23) Sep 02 2009 I also feel the bikeshed should be colored 'Maybe'.
- Jarrett Billingsley (6/32) Sep 02 2009 's
- Andrei Alexandrescu (5/34) Sep 02 2009 Great. Now, before we get all jolly about Maybe, let me point out that
- Danny Wilson (5/43) Sep 02 2009 So if pointers wouldn't be considered evil, Maybe!T* would suffice?
- Simen Kjaeraas (6/9) Sep 02 2009 Well, for one, we don't want to disallow pointer arithmetic. Then, we
- Andrei Alexandrescu (5/53) Sep 02 2009 Ref means lvalue of type T. Pointer is a type distinct from T. So
- Danny Wilson (7/14) Sep 02 2009 Thanks. I googled first but couldn't find some explicit documentation
- Andrei Alexandrescu (6/28) Sep 02 2009 I'd love for that to work, but ref T is not a type. "ref" is a storage
- Jarrett Billingsley (4/7) Sep 02 2009 But reference types already are nullable. Unless you mean for
- grauzone (4/18) Sep 02 2009 I still don't understand how one can feel comfortable with the fact,
- Andrei Alexandrescu (4/15) Sep 02 2009 That's why I want to add no member functions to Optional. The test for
- Rainer Deyke (5/7) Sep 02 2009 I don't you can implement Optional without at least one (possibly
- Steven Schveighoffer (7/20) Sep 03 2009 How does Optional!valuetype support this:
- Andrei Alexandrescu (3/29) Sep 03 2009 I should have said: no *named* member. Operators and cdtors are fair gam...
- bearophile (7/10) Sep 02 2009 It looks good and simple enough. The name may be "Nullable" or "Maybe" o...
- Jeremie Pelletier (9/24) Sep 02 2009 I just recently converted tons of COM headers in win32 to D (gotta love ...
- Jarrett Billingsley (5/29) Sep 02 2009 Hmm, you know this could be handled raaaaaather niiiiicely with attribut...
- Rainer Deyke (6/14) Sep 02 2009 You need some syntactic way to distinguish the contained value from the
- Andrei Alexandrescu (4/17) Sep 02 2009 Interesting. I wonder whether it's better to fold Optional!(Optional!T)
- Rainer Deyke (15/22) Sep 02 2009 That would be semantically incorrect. Optional!T must be able to hold
- Steven Schveighoffer (8/29) Sep 03 2009 How does one distinguish an Optional!(Optional!int) that is null from an...
- Rainer Deyke (9/14) Sep 03 2009 Your conclusion is backwards here. In order for Optional!T to be
- Steven Schveighoffer (21/29) Sep 04 2009 I disagree, how many ways do you want to say something is unset? I thin...
- Rainer Deyke (16/23) Sep 04 2009 Possible use case for Optional: storing an upper limit. In this case
- Steven Schveighoffer (22/43) Sep 08 2009 Why do you care if it was or was not found in the configuration file
- Rainer Deyke (19/45) Sep 08 2009 Yes, in this particular case, passing a default value would work just as
- Steven Schveighoffer (23/56) Sep 08 2009 You're reaching here :) A default configuration value that is expensive...
- Rainer Deyke (19/42) Sep 08 2009 New example:
- Rainer Deyke (4/14) Sep 08 2009 --
- Steven Schveighoffer (23/65) Sep 10 2009 Another bad example.
- Rainer Deyke (7/20) Sep 10 2009 If cached_maximum and cache_valid are the only members of
- Steven Schveighoffer (17/25) Sep 10 2009 Why not?
- Rainer Deyke (11/17) Sep 10 2009 Because that's horribly non-orthogonal code. I go by the rule that
- Steven Schveighoffer (6/18) Sep 10 2009 Sure, do whatever you want for your *private data members*. I see too
- Rainer Deyke (36/39) Sep 10 2009 And I see no value at all, but a lot of danger. Even ignoring, what
- Steven Schveighoffer (13/52) Sep 10 2009 Doesn't compile. Implicit casting doesn't mean implicit reference casti...
- Rainer Deyke (30/87) Sep 10 2009 OK, this is just an annoyance, not actual danger. It's still an
- Steven Schveighoffer (24/49) Sep 11 2009 Just like defaultInitialize(double_value) is different than
- Andrei Alexandrescu (5/17) Sep 11 2009 My experience with wrappers in C++ has been similar to Rainer's.
- Steven Schveighoffer (9/25) Sep 11 2009 My experience with Proxy objects in .Net remoting has been good. Grante...
- Andrei Alexandrescu (12/57) Sep 11 2009 The value is in the fact that you can use an Optional!T as a substitute
- Max Samukha (13/28) Sep 03 2009 I prefer Nullable.
- Leandro Lucarella (13/21) Sep 03 2009 Maybe this is a very silly question, but what is exactly the difference
- Steven Schveighoffer (7/23) Sep 03 2009 The difference is you don't have to store the T somewhere else. That is...
- Andrei Alexandrescu (5/32) Sep 03 2009 Oh, that I forgot and is essential! A closer question is what's the
- Leandro Lucarella (11/31) Sep 03 2009 Ok, this seems like a reasonable point. So Optional!T is a value type,
- Steven Schveighoffer (8/20) Sep 03 2009 There are performance concerns, you need 16 bytes minimum to store a val...
- Andrei Alexandrescu (5/17) Sep 03 2009 It's a good question. There are two differences: (a) Optional!T behaves...
- Andrei Alexandrescu (4/27) Sep 03 2009 The answer above is incorrect. I actually answered about OptionalRef!T.
- Leandro Lucarella (12/28) Sep 03 2009 (a) And *o behaves *exactly* like T, that doesn't seems like an advantag...
- Steven Schveighoffer (32/41) Sep 03 2009 I like nullable, but optional will work.
- Jarrett Billingsley (15/58) Sep 03 2009 s
- Steven Schveighoffer (12/21) Sep 03 2009 I just thought of something else. If you use alias this, then what
- Denis Koroskin (3/13) Sep 03 2009 Nullable, unless this feature is planned for value types only (please, n...
- Daniel Keep (62/77) Sep 08 2009 Given the apocalyptic battle raging in this thread at the moment, maybe
- Andrei Alexandrescu (5/39) Sep 09 2009 [snip]
Boost's Optional). Apparently a good design is to define Optional!T with a minimum of member functions (ideally none) and have it use the "alias this" feature to masquerade as a T. That way Optional!T looks and feels much like a T, except that it supports a function bool isNull(T)(Optional!T value); Am I on the right track? If so, what is the name you'd prefer for this artifact? Andrei
Sep 02 2009
Op Wed, 02 Sep 2009 21:39:28 +0200 schreef Andrei Alexandrescu <SeeWebsiteForEmail erdani.org>:Boost's Optional). Apparently a good design is to define Optional!T with a minimum of member functions (ideally none) and have it use the "alias this" feature to masquerade as a T. That way Optional!T looks and feels much like a T, except that it supports a function bool isNull(T)(Optional!T value); Am I on the right track? If so, what is the name you'd prefer for this artifact? AndreiHow about: Maybe!T Got that from Haskell :-)
Sep 02 2009
Danny Wilson <bluezenix gmail.com> wrote:Op Wed, 02 Sep 2009 21:39:28 +0200 schreef Andrei Alexandrescu <SeeWebsiteForEmail erdani.org>:I also feel the bikeshed should be colored 'Maybe'. -- SimenBoost's Optional). Apparently a good design is to define Optional!T with a minimum of member functions (ideally none) and have it use the "alias this" feature to masquerade as a T. That way Optional!T looks and feels much like a T, except that it supports a function bool isNull(T)(Optional!T value); Am I on the right track? If so, what is the name you'd prefer for this artifact? AndreiHow about: Maybe!T Got that from Haskell :-)
Sep 02 2009
On Wed, Sep 2, 2009 at 4:13 PM, Simen Kjaeraas<simen.kjaras gmail.com> wrot= e:Danny Wilson <bluezenix gmail.com> wrote:'sOp Wed, 02 Sep 2009 21:39:28 +0200 schreef Andrei Alexandrescu <SeeWebsiteForEmail erdani.org>:erOptional). Apparently a good design is to define Optional!T with a minimum of memb=xceptfunctions (ideally none) and have it use the "alias this" feature to masquerade as a T. That way Optional!T looks and feels much like a T, e=Thirded!I also feel the bikeshed should be colored 'Maybe'.that it supports a function bool isNull(T)(Optional!T value); Am I on the right track? If so, what is the name you'd prefer for this artifact? AndreiHow about: =A0Maybe!T Got that from Haskell :-)
Sep 02 2009
Jarrett Billingsley wrote:On Wed, Sep 2, 2009 at 4:13 PM, Simen Kjaeraas<simen.kjaras gmail.com> wrote:Great. Now, before we get all jolly about Maybe, let me point out that we also need the "ref" corresponding type. And OptionalRef and NullableRef may sound better to some than MaybeRef. AndreiDanny Wilson <bluezenix gmail.com> wrote:Thirded!Op Wed, 02 Sep 2009 21:39:28 +0200 schreef Andrei Alexandrescu <SeeWebsiteForEmail erdani.org>:I also feel the bikeshed should be colored 'Maybe'.Optional). Apparently a good design is to define Optional!T with a minimum of member functions (ideally none) and have it use the "alias this" feature to masquerade as a T. That way Optional!T looks and feels much like a T, except that it supports a function bool isNull(T)(Optional!T value); Am I on the right track? If so, what is the name you'd prefer for this artifact? AndreiHow about: Maybe!T Got that from Haskell :-)
Sep 02 2009
Op Wed, 02 Sep 2009 22:20:04 +0200 schreef Andrei Alexandrescu <SeeWebsiteForEmail erdani.org>:Jarrett Billingsley wrote:So if pointers wouldn't be considered evil, Maybe!T* would suffice? Can someone point me out what the big difference is between ref and simply disallowing pointer arithmitic? Is it marketing?On Wed, Sep 2, 2009 at 4:13 PM, Simen Kjaeraas<simen.kjaras gmail.com> wrote:Great. Now, before we get all jolly about Maybe, let me point out that we also need the "ref" corresponding type. And OptionalRef and NullableRef may sound better to some than MaybeRef. AndreiDanny Wilson <bluezenix gmail.com> wrote:Thirded!Op Wed, 02 Sep 2009 21:39:28 +0200 schreef Andrei Alexandrescu <SeeWebsiteForEmail erdani.org>:I also feel the bikeshed should be colored 'Maybe'.Boost's Optional). Apparently a good design is to define Optional!T with a minimum of member functions (ideally none) and have it use the "alias this" feature to masquerade as a T. That way Optional!T looks and feels much like a T, except that it supports a function bool isNull(T)(Optional!T value); Am I on the right track? If so, what is the name you'd prefer for this artifact? AndreiHow about: Maybe!T Got that from Haskell :-)
Sep 02 2009
Danny Wilson <bluezenix gmail.com> wrote:So if pointers wouldn't be considered evil, Maybe!T* would suffice? Can someone point me out what the big difference is between ref and simply disallowing pointer arithmitic? Is it marketing?Well, for one, we don't want to disallow pointer arithmetic. Then, we introduce pointers without pointer arithmetic, and call them references. Best of both worlds. -- Simen
Sep 02 2009
Danny Wilson wrote:Op Wed, 02 Sep 2009 22:20:04 +0200 schreef Andrei Alexandrescu <SeeWebsiteForEmail erdani.org>:Ref means lvalue of type T. Pointer is a type distinct from T. So although NullableRef!T is substitutable for an lvalue of type T, Nullable!(T*) is not. AndreiJarrett Billingsley wrote:So if pointers wouldn't be considered evil, Maybe!T* would suffice? Can someone point me out what the big difference is between ref and simply disallowing pointer arithmitic? Is it marketing?On Wed, Sep 2, 2009 at 4:13 PM, Simen Kjaeraas<simen.kjaras gmail.com> wrote:Great. Now, before we get all jolly about Maybe, let me point out that we also need the "ref" corresponding type. And OptionalRef and NullableRef may sound better to some than MaybeRef. AndreiDanny Wilson <bluezenix gmail.com> wrote:Thirded!Op Wed, 02 Sep 2009 21:39:28 +0200 schreef Andrei Alexandrescu <SeeWebsiteForEmail erdani.org>:I also feel the bikeshed should be colored 'Maybe'.Boost's Optional). Apparently a good design is to define Optional!T with a minimum of member functions (ideally none) and have it use the "alias this" feature to masquerade as a T. That way Optional!T looks and feels much like a T, except that it supports a function bool isNull(T)(Optional!T value); Am I on the right track? If so, what is the name you'd prefer for this artifact? AndreiHow about: Maybe!T Got that from Haskell :-)
Sep 02 2009
Op Wed, 02 Sep 2009 22:55:53 +0200 schreef Andrei Alexandrescu <SeeWebsiteForEmail erdani.org>:Thanks. I googled first but couldn't find some explicit documentation about 'ref' just it being mentioned here and there :-) Are there any problems with something like: Nullable!(ref T) ?So if pointers wouldn't be considered evil, Maybe!T* would suffice? Can someone point me out what the big difference is between ref and simply disallowing pointer arithmitic? Is it marketing?Ref means lvalue of type T. Pointer is a type distinct from T. So although NullableRef!T is substitutable for an lvalue of type T, Nullable!(T*) is not. Andrei
Sep 02 2009
Danny Wilson wrote:Op Wed, 02 Sep 2009 22:55:53 +0200 schreef Andrei Alexandrescu <SeeWebsiteForEmail erdani.org>:I'd love for that to work, but ref T is not a type. "ref" is a storage class that's allowed only in a function declaration context. (What "ref" means in that context is pass or return by reference as opposed to the default pass by value.) AndreiThanks. I googled first but couldn't find some explicit documentation about 'ref' just it being mentioned here and there :-) Are there any problems with something like: Nullable!(ref T) ?So if pointers wouldn't be considered evil, Maybe!T* would suffice? Can someone point me out what the big difference is between ref and simply disallowing pointer arithmitic? Is it marketing?Ref means lvalue of type T. Pointer is a type distinct from T. So although NullableRef!T is substitutable for an lvalue of type T, Nullable!(T*) is not. Andrei
Sep 02 2009
On Wed, Sep 2, 2009 at 4:20 PM, Andrei Alexandrescu<SeeWebsiteForEmail erdani.org> wrote:Great. Now, before we get all jolly about Maybe, let me point out that we also need the "ref" corresponding type. And OptionalRef and NullableRef may sound better to some than MaybeRef.But reference types already are nullable. Unless you mean for something like NullableRef!int.
Sep 02 2009
Andrei Alexandrescu wrote:Boost's Optional). Apparently a good design is to define Optional!T with a minimum of member functions (ideally none) and have it use the "alias this" feature to masquerade as a T. That way Optional!T looks and feels much like a T, except that it supports a functionI still don't understand how one can feel comfortable with the fact, that "alias this" can overshadow arbitrary members of the alias'ed type. Am I missing something?bool isNull(T)(Optional!T value); Am I on the right track? If so, what is the name you'd prefer for this artifact? Andrei
Sep 02 2009
grauzone wrote:Andrei Alexandrescu wrote:That's why I want to add no member functions to Optional. The test for null will be a free function. AndreiBoost's Optional). Apparently a good design is to define Optional!T with a minimum of member functions (ideally none) and have it use the "alias this" feature to masquerade as a T. That way Optional!T looks and feels much like a T, except that it supports a functionI still don't understand how one can feel comfortable with the fact, that "alias this" can overshadow arbitrary members of the alias'ed type.
Sep 02 2009
Andrei Alexandrescu wrote:That's why I want to add no member functions to Optional. The test for null will be a free function.I don't you can implement Optional without at least one (possibly private) data member. Does 'alias this' shadow private data members? -- Rainer Deyke - rainerd eldwood.com
Sep 02 2009
On Wed, 02 Sep 2009 16:54:30 -0400, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:grauzone wrote:How does Optional!valuetype support this: Optional!valuetype x; x = null; Don't you need opAssign? -SteveAndrei Alexandrescu wrote:That's why I want to add no member functions to Optional. The test for null will be a free function.Boost's Optional). Apparently a good design is to define Optional!T with a minimum of member functions (ideally none) and have it use the "alias this" feature to masquerade as a T. That way Optional!T looks and feels much like a T, except that it supports a functionI still don't understand how one can feel comfortable with the fact, that "alias this" can overshadow arbitrary members of the alias'ed type.
Sep 03 2009
Steven Schveighoffer wrote:On Wed, 02 Sep 2009 16:54:30 -0400, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:I should have said: no *named* member. Operators and cdtors are fair game. Andreigrauzone wrote:How does Optional!valuetype support this: Optional!valuetype x; x = null; Don't you need opAssign? -SteveAndrei Alexandrescu wrote:That's why I want to add no member functions to Optional. The test for null will be a free function.Boost's Optional). Apparently a good design is to define Optional!T with a minimum of member functions (ideally none) and have it use the "alias this" feature to masquerade as a T. That way Optional!T looks and feels much like a T, except that it supports a functionI still don't understand how one can feel comfortable with the fact, that "alias this" can overshadow arbitrary members of the alias'ed type.
Sep 03 2009
Andrei Alexandrescu:bool isNull(T)(Optional!T value); Am I on the right track? If so, what is the name you'd prefer for this artifact?It looks good and simple enough. The name may be "Nullable" or "Maybe" or something like that. This looks like a good use case of the alias this" feature. Such Nullable() may contain a static if that avoids the presence of the internal boolean field if T is already a pointer or class reference, saving memory. And isNull() may work with pointer/class references too in input. But such Nullable struct has to be designed thinking about the future too: I hope in future D2 will have nonull class references (a feature that's probably 5-10 times more useful than the Nullable wrapper), so you need a "nullable" or nullable keyword or annotation to specify that a class reference can be null. In such situation it may be possible for the nullable annotation to work on values too (in this case it wraps them into a Nullable struct). I don't know if such double usage of nullable can lead to problems, I think not. A possible future compiler optimization for arrays of Nullables is to split it into two arrays: an array of just the wrapped type, plus a bitvector to represent what items are null. But arrays of nullables probably will not be common enough to deserve such optimization... Bye, bearophile
Sep 02 2009
Andrei Alexandrescu Wrote:Boost's Optional). Apparently a good design is to define Optional!T with a minimum of member functions (ideally none) and have it use the "alias this" feature to masquerade as a T. That way Optional!T looks and feels much like a T, except that it supports a function bool isNull(T)(Optional!T value); Am I on the right track? If so, what is the name you'd prefer for this artifact? AndreiI just recently converted tons of COM headers in win32 to D (gotta love extern(C++)) and I really like how they hint the compiler of what parameters are used for. They have all sorts of macros like __in, __inout, __out, __in_opt, __inout_opt, __out_opt. Why can't these be used in D too and implicitly add the appropriate contracts to the function: void foo(in_opt int* a) { ... } can be the same as void foo(in int* a) in { assert(a); } body { ... } I know I'm trying to push a lot of library stuff to the language spec, but it would just be so much more convenient that way. in_opt would be semantically the same as in, with the added contract.
Sep 02 2009
On Wed, Sep 2, 2009 at 8:20 PM, Jeremie Pelletier<jeremiep gmail.com> wrote:Andrei Alexandrescu Wrote:Hmm, you know this could be handled raaaaaather niiiiicely with attributes. void foo( in_opt int* a) { ... } But they don't have any compelling use cases, right? they just keep showing up, huhBoost's Optional). Apparently a good design is to define Optional!T with a minimum of member functions (ideally none) and have it use the "alias this" feature to masquerade as a T. That way Optional!T looks and feels much like a T, except that it supports a function bool isNull(T)(Optional!T value); Am I on the right track? If so, what is the name you'd prefer for this artifact? AndreiI just recently converted tons of COM headers in win32 to D (gotta love extern(C++)) and I really like how they hint the compiler of what parameters are used for. They have all sorts of macros like __in, __inout, __out, __in_opt, __inout_opt, __out_opt. Why can't these be used in D too and implicitly add the appropriate contracts to the function: void foo(in_opt int* a) { ... } can be the same as void foo(in int* a) in { assert(a); } body { ... } I know I'm trying to push a lot of library stuff to the language spec, but it would just be so much more convenient that way. in_opt would be semantically the same as in, with the added contract.
Sep 02 2009
Andrei Alexandrescu wrote:Apparently a good design is to define Optional!T with a minimum of member functions (ideally none) and have it use the "alias this" feature to masquerade as a T. That way Optional!T looks and feels much like a T, except that it supports a function bool isNull(T)(Optional!T value); Am I on the right track?You need some syntactic way to distinguish the contained value from the container. Using "alias this" seems messy here. Optional!Optional!T is both valid and likely to occur. -- Rainer Deyke - rainerd eldwood.com
Sep 02 2009
Rainer Deyke wrote:Andrei Alexandrescu wrote:Interesting. I wonder whether it's better to fold Optional!(Optional!T) into Optional!T. AndreiApparently a good design is to define Optional!T with a minimum of member functions (ideally none) and have it use the "alias this" feature to masquerade as a T. That way Optional!T looks and feels much like a T, except that it supports a function bool isNull(T)(Optional!T value); Am I on the right track?You need some syntactic way to distinguish the contained value from the container. Using "alias this" seems messy here. Optional!Optional!T is both valid and likely to occur.
Sep 02 2009
Andrei Alexandrescu wrote:Rainer Deyke wrote:That would be semantically incorrect. Optional!T must be able to hold all possible values of T, plus a distinct null value. Consider: Optional!int upper_limit; Optional!(Optional!int) config_limit = config_file.get("upper_limit"); if (isNull(config_limit)) { upper_limit = default_upper_limit; } else { upper_limit = config_limit; // Could be null. } Then consider that this could be in a template function, with Optional!int supplied as a template argument. -- Rainer Deyke - rainerd eldwood.comYou need some syntactic way to distinguish the contained value from the container. Using "alias this" seems messy here. Optional!Optional!T is both valid and likely to occur.Interesting. I wonder whether it's better to fold Optional!(Optional!T) into Optional!T.
Sep 02 2009
On Thu, 03 Sep 2009 02:36:12 -0400, Rainer Deyke <rainerd eldwood.com> wrote:Andrei Alexandrescu wrote:How does one distinguish an Optional!(Optional!int) that is null from an Optional!(Optional!int) whose Optional!int is null? If there's no way to test for it, then they are not conceivably different. I think Andrei is right here, for any type T that already is nullable (including pointer and reference types), Optional!T should alias to T. -SteveRainer Deyke wrote:That would be semantically incorrect. Optional!T must be able to hold all possible values of T, plus a distinct null value. Consider: Optional!int upper_limit; Optional!(Optional!int) config_limit = config_file.get("upper_limit"); if (isNull(config_limit)) { upper_limit = default_upper_limit; } else { upper_limit = config_limit; // Could be null. } Then consider that this could be in a template function, with Optional!int supplied as a template argument.You need some syntactic way to distinguish the contained value from the container. Using "alias this" seems messy here. Optional!Optional!T is both valid and likely to occur.Interesting. I wonder whether it's better to fold Optional!(Optional!T) into Optional!T.
Sep 03 2009
Steven Schveighoffer wrote:How does one distinguish an Optional!(Optional!int) that is null from an Optional!(Optional!int) whose Optional!int is null? If there's no way to test for it, then they are not conceivably different.That's the fatal flaw in Andrei's design that I was pointing out, yes.I think Andrei is right here, for any type T that already is nullable (including pointer and reference types), Optional!T should alias to T.Your conclusion is backwards here. In order for Optional!T to be useful, it must be able to hold all possible of T plus a distinct null value. If Optional!T cannot do that for T = Optional!T2, then Optional!T is useless in generic programming, bordering on useless in general. I think my example program demonstrated this amply. -- Rainer Deyke - rainerd eldwood.com
Sep 03 2009
On Thu, 03 Sep 2009 16:20:41 -0400, Rainer Deyke <rainerd eldwood.com> wrote:Steven Schveighoffer wrote:I disagree, how many ways do you want to say something is unset? I think one case -- is this thing null -- is plenty for 99% of cases. Your case, well, I had trouble understanding it, but I think the nuance of how something is unset is not usually important. I think that what you will find with a solution that *does* allow this, you will be sacrificing something else -- such as syntax clarity or a member function. If you have specific requirements for "is this set to nulla or nullb," then I think that's a more complicated problem to solve, one which requires more complicated structures than what is being proposed here. I don't think we should cater a feature like this to the one time somebody needs 8 different names for snow...err null. What I would *love* to see is a way to intercept these statements, and handle them specially: x is null; x = null; That doesn't fit your problem at all, since there is only one keyword named null. -SteveI think Andrei is right here, for any type T that already is nullable (including pointer and reference types), Optional!T should alias to T.Your conclusion is backwards here. In order for Optional!T to be useful, it must be able to hold all possible of T plus a distinct null value. If Optional!T cannot do that for T = Optional!T2, then Optional!T is useless in generic programming, bordering on useless in general. I think my example program demonstrated this amply.
Sep 04 2009
Steven Schveighoffer wrote:I disagree, how many ways do you want to say something is unset? I think one case -- is this thing null -- is plenty for 99% of cases. Your case, well, I had trouble understanding it, but I think the nuance of how something is unset is not usually important.Possible use case for Optional: storing an upper limit. In this case "null" means "there is no upper limit". Possible use case for Optional: a function that retrieves a value from a configuration file. In this case, "null" means "the value is not found in the configuration file". So what happens if you want to store that there is no upper limit in a configuration file?I think that what you will find with a solution that *does* allow this, you will be sacrificing something else -- such as syntax clarity or a member function.No, keeping the container and the containee separate leads to /clearer/ (if slightly more verbose) syntax. My proposal is to use use pointer syntax: Optional!T v; f(v); // Do something with the container. f(*v); // Do something with the containee. -- Rainer Deyke - rainerd eldwood.com
Sep 04 2009
On Fri, 04 Sep 2009 22:45:40 -0400, Rainer Deyke <rainerd eldwood.com> wrote:Steven Schveighoffer wrote:Why do you care if it was or was not found in the configuration file beyond the call to retrieve it? Pass in a default value (of null if you wish), or have the function return a separate boolean indicating whether it was found or not. Using a new type just to determine if something was found during a function call seems like a waste to me. If there's a reason to store that fact beyond the function call, then create a new type which stores that fact. You shouldn't complicate the syntax for the sake of a corner case. What do you think is more confusing? auto val = getConfig("upperlimit") if(val is null) upperLimit = default_upper_limit; else upperLimit = *val; - or - upperLimit = getConfig("upperlimit", default_upper_limit);I disagree, how many ways do you want to say something is unset? I think one case -- is this thing null -- is plenty for 99% of cases. Your case, well, I had trouble understanding it, but I think the nuance of how something is unset is not usually important.Possible use case for Optional: storing an upper limit. In this case "null" means "there is no upper limit". Possible use case for Optional: a function that retrieves a value from a configuration file. In this case, "null" means "the value is not found in the configuration file". So what happens if you want to store that there is no upper limit in a configuration file?Hm... that means you have to use *v every time you want the value, and just v if you want to check for null? I think the most common case is using the containee, I'd rather have that be the default. -SteveI think that what you will find with a solution that *does* allow this, you will be sacrificing something else -- such as syntax clarity or a member function.No, keeping the container and the containee separate leads to /clearer/ (if slightly more verbose) syntax. My proposal is to use use pointer syntax: Optional!T v; f(v); // Do something with the container. f(*v); // Do something with the containee.
Sep 08 2009
Steven Schveighoffer wrote:Why do you care if it was or was not found in the configuration file beyond the call to retrieve it? Pass in a default value (of null if you wish), or have the function return a separate boolean indicating whether it was found or not.Yes, in this particular case, passing a default value would work just as well. But what if you want to cache the contents of the entire configuration file? What if calculating the default value is an expensive operation that should be avoided if possible? My argument isn't about any specific example. My argument is that inconsistent behavior in corner cases is the bane of good programming.Using a new type just to determine if something was found during a function call seems like a waste to me. If there's a reason to store that fact beyond the function call, then create a new type which stores that fact.I thought the whole point of the Optional library type was to remove the need for user-defined types consisting of a boolean and a T, where the T is only valid if the boolean is 'true'? If the real answer is to write a new, different type, why have Optional at all?What do you think is more confusing? auto val = getConfig("upperlimit") if(val is null) upperLimit = default_upper_limit; else upperLimit = *val;Not confusing.upperLimit = getConfig("upperlimit", default_upper_limit);Also not confusing, but different. 'default_upper_limit' is evaluated even if the value is found in the configuration file.You can't have that, because 'v' is already the container. The best you can get is a proxy that sometimes acts like a containee, but really isn't. This kind of sloppy thinking has no place in computer programming. -- Rainer Deyke - rainerd eldwood.comNo, keeping the container and the containee separate leads to /clearer/ (if slightly more verbose) syntax. My proposal is to use use pointer syntax: Optional!T v; f(v); // Do something with the container. f(*v); // Do something with the containee.Hm... that means you have to use *v every time you want the value, and just v if you want to check for null? I think the most common case is using the containee, I'd rather have that be the default.
Sep 08 2009
On Tue, 08 Sep 2009 15:50:15 -0400, Rainer Deyke <rainerd eldwood.com> wrote:Steven Schveighoffer wrote:You're reaching here :) A default configuration value that is expensive to calculate?Why do you care if it was or was not found in the configuration file beyond the call to retrieve it? Pass in a default value (of null if you wish), or have the function return a separate boolean indicating whether it was found or not.Yes, in this particular case, passing a default value would work just as well. But what if you want to cache the contents of the entire configuration file? What if calculating the default value is an expensive operation that should be avoided if possible?My argument isn't about any specific example. My argument is that inconsistent behavior in corner cases is the bane of good programming.I understand what you are saying, but often times (as I have learned from my own crusades on the NG), when it is hard to find good cases for an argument, it means the argument is superficial or academic. I could very well be wrong, but to me, the practicality of the offered use cases is important when evaluating whether to complicate syntax. It's like saying that all integers should be arbitrary precision, because sometimes you need arbitrary precision integers. The better answer is BigInt. If you need arbitrary precision, use that, otherwise int is good enough for most cases.I thought it was to provide a way to make a value type have an "unset" value. There is no reason in my opinion to make *all* uses of the new type be able to represent multiple values for "unset". If that is what you want, then I think it can be done, but it should not affect the syntax for the majority of cases.Using a new type just to determine if something was found during a function call seems like a waste to me. If there's a reason to store that fact beyond the function call, then create a new type which stores that fact.I thought the whole point of the Optional library type was to remove the need for user-defined types consisting of a boolean and a T, where the T is only valid if the boolean is 'true'? If the real answer is to write a new, different type, why have Optional at all?You mean like struct pointers? Generic type wrappers? Remote object proxies? :P Really, what I think we want to do is add a new operation to a type -- "is null". Not two operations, "is null" and "get containee". -SteveYou can't have that, because 'v' is already the container. The best you can get is a proxy that sometimes acts like a containee, but really isn't. This kind of sloppy thinking has no place in computer programming.No, keeping the container and the containee separate leads to /clearer/ (if slightly more verbose) syntax. My proposal is to use use pointer syntax: Optional!T v; f(v); // Do something with the container. f(*v); // Do something with the containee.Hm... that means you have to use *v every time you want the value, and just v if you want to check for null? I think the most common case is using the containee, I'd rather have that be the default.
Sep 08 2009
Steven Schveighoffer wrote:On Tue, 08 Sep 2009 15:50:15 -0400, Rainer Deyke <rainerd eldwood.com> wrote:New example: struct AlgebraicFunction { double getMaximum() { if (isNull(this.cachedMaximum)) { this.cachedMaximum = this.veryExpensiveCalculation(); } return *this.cachedMaximum; } private Optional!(Optional!double) cached_maximum; }Yes, in this particular case, passing a default value would work just as well. But what if you want to cache the contents of the entire configuration file? What if calculating the default value is an expensive operation that should be avoided if possible?You're reaching here :) A default configuration value that is expensive to calculate?I understand what you are saying, but often times (as I have learned from my own crusades on the NG), when it is hard to find good cases for an argument, it means the argument is superficial or academic. I could very well be wrong, but to me, the practicality of the offered use cases is important when evaluating whether to complicate syntax.I think it's much more important to have clear consistent semantics than convenient syntactic shortcuts. I don't want D to turn into Perl.I thought it was to provide a way to make a value type have an "unset" value.The point is to have a type that can represent all possible values of T, plus an additional singular value, for all possible types of T.These examples of proxies are also a bad idea. When I want to access a member of a struct through a pointer, I use '(*p).x'. -- Rainer Deyke - rainerd eldwood.comYou can't have that, because 'v' is already the container. The best you can get is a proxy that sometimes acts like a containee, but really isn't. This kind of sloppy thinking has no place in computer programming.You mean like struct pointers? Generic type wrappers? Remote object proxies? :P
Sep 08 2009
Rainer Deyke wrote:struct AlgebraicFunction { double getMaximum() {This 'double' was obviously supposed to be 'Optional!double'.if (isNull(this.cachedMaximum)) { this.cachedMaximum = this.veryExpensiveCalculation(); } return *this.cachedMaximum; } private Optional!(Optional!double) cachedMaximum; }-- Rainer Deyke - rainerd eldwood.com
Sep 08 2009
On Tue, 08 Sep 2009 19:24:31 -0400, Rainer Deyke <rainerd eldwood.com> wrote:Steven Schveighoffer wrote:Another bad example. struct AlgebraicFunction { double getMaximum() { if (!cache_valid) { this.cachedMaximum = this.veryExpensiveCalculation(); cache_valid = true; } return this.cachedMaximum; // hey, I thought you only use struct pointers like (*x).member? } private Optional!double cached_maximum; private bool cache_valid = false; }On Tue, 08 Sep 2009 15:50:15 -0400, Rainer Deyke <rainerd eldwood.com> wrote:New example: struct AlgebraicFunction { double getMaximum() { if (isNull(this.cachedMaximum)) { this.cachedMaximum = this.veryExpensiveCalculation(); } return *this.cachedMaximum; } private Optional!(Optional!double) cached_maximum; }Yes, in this particular case, passing a default value would work just as well. But what if you want to cache the contents of the entire configuration file? What if calculating the default value is an expensive operation that should be avoided if possible?You're reaching here :) A default configuration value that is expensive to calculate?It is clear and consistent, it's just not defined how you would like. What you want does not bring anything significant to the table as far as the vast majority of cases.I understand what you are saying, but often times (as I have learned from my own crusades on the NG), when it is hard to find good cases for an argument, it means the argument is superficial or academic. I could very well be wrong, but to me, the practicality of the offered use cases is important when evaluating whether to complicate syntax.I think it's much more important to have clear consistent semantics than convenient syntactic shortcuts. I don't want D to turn into Perl.Here we simply disagree.I thought it was to provide a way to make a value type have an "unset" value.The point is to have a type that can represent all possible values of T, plus an additional singular value, for all possible types of T.Then you are going against well established programming techniques already in place in D. Not that you aren't allowed to have your opinion, but I don't think it's going to get much agreement from D's developers. -SteveThese examples of proxies are also a bad idea. When I want to access a member of a struct through a pointer, I use '(*p).x'.You can't have that, because 'v' is already the container. The best you can get is a proxy that sometimes acts like a containee, but really isn't. This kind of sloppy thinking has no place in computer programming.You mean like struct pointers? Generic type wrappers? Remote object proxies? :P
Sep 10 2009
Steven Schveighoffer wrote:struct AlgebraicFunction { double getMaximum() { if (!cache_valid) { this.cachedMaximum = this.veryExpensiveCalculation(); cache_valid = true; } return this.cachedMaximum; // hey, I thought you only use struct pointers like (*x).member?I kind of forgot that 'this' is a pointer inside structs.} private Optional!double cached_maximum; private bool cache_valid = false; }If cached_maximum and cache_valid are the only members of AlgebraicFunction, you might be able to get away with that. If you have several different cached values, not so much. -- Rainer Deyke - rainerd eldwood.com
Sep 10 2009
On Thu, 10 Sep 2009 12:47:35 -0400, Rainer Deyke <rainerd eldwood.com> wrote:Why not? private Optional!double cached_x; private bool cachex_valid; private Optional!double cached_y; private bool cachey_valid; I guess my point really should be that storing a boolean next to a value isn't any different than storing them inside a struct with two distinct ways of getting at them, especially as a privately cached value where the interface isn't affected. What makes Andrei's proposal compelling is that you can drop it into any place where a value type or reference type is normally used (including non-template functions) and it just works. Your method doesn't do that, you need the dereference operator to get at the value type, so the code needs to be specifically built to handle your "wrapper" type. -Steve} private Optional!double cached_maximum; private bool cache_valid = false; }If cached_maximum and cache_valid are the only members of AlgebraicFunction, you might be able to get away with that. If you have several different cached values, not so much.
Sep 10 2009
Steven Schveighoffer wrote:Why not? private Optional!double cached_x; private bool cachex_valid; private Optional!double cached_y; private bool cachey_valid;Because that's horribly non-orthogonal code. I go by the rule that whenever two pieces of data are more closely associated with each other members of the enclosing data structure, then those pieces of data must be factored out into their own data structure. *Especially* if, as in this case, the resulting data structure is reusable. Ideally, the entire logic of calculating values on demand and then caching them should be factored out, but that's a bit harder to do in this case. -- Rainer Deyke - rainerd eldwood.com
Sep 10 2009
On Thu, 10 Sep 2009 14:11:55 -0400, Rainer Deyke <rainerd eldwood.com> wrote:Steven Schveighoffer wrote:Sure, do whatever you want for your *private data members*. I see too much value in Optional!T being aliased to the underlying T to go for the completely generic solution which requires special syntax to get at the T. -SteveWhy not? private Optional!double cached_x; private bool cachex_valid; private Optional!double cached_y; private bool cachey_valid;Because that's horribly non-orthogonal code. I go by the rule that whenever two pieces of data are more closely associated with each other members of the enclosing data structure, then those pieces of data must be factored out into their own data structure. *Especially* if, as in this case, the resulting data structure is reusable.
Sep 10 2009
Steven Schveighoffer wrote:Sure, do whatever you want for your *private data members*. I see too much value in Optional!T being aliased to the underlying T to go for the completely generic solution which requires special syntax to get at the T.And I see no value at all, but a lot of danger. Even ignoring, what about this: void f(ref int v) { // Do something... } void g() { Optional!int x = 5; f(x); } Or this: void report_error(int); void f(T)(out T v) { if (error_condition) throw new SomeException(); v = something; } void g() { Optional!int v = someCalculation(); if (!isNull(v)) { try { f(v); } catch (SomeException) { report_error(v); // Oops, invalid: 'v' turned into null. } } } Note how in the second example, the semantics of the function 'f' subtly change depending on whether the argument is an 'int' or and 'Optional!int'. Proxies, especially proxies that add functionality to their base type, cannot act exactly like the type they are proxying. Using a proxy therefore requires the significant mental overhead of keeping track of all the corner cases in which the proxy does not act like the type it is proxying, as well as the hassle of working around those limitation whenever they come up. -- Rainer Deyke - rainerd eldwood.com
Sep 10 2009
On Thu, 10 Sep 2009 17:41:19 -0400, Rainer Deyke <rainerd eldwood.com> wrote:Steven Schveighoffer wrote:Doesn't compile. Implicit casting doesn't mean implicit reference casting.Sure, do whatever you want for your *private data members*. I see too much value in Optional!T being aliased to the underlying T to go for the completely generic solution which requires special syntax to get at the T.And I see no value at all, but a lot of danger. Even ignoring, what about this: void f(ref int v) { // Do something... } void g() { Optional!int x = 5; f(x); }Or this: void report_error(int); void f(T)(out T v) { if (error_condition) throw new SomeException(); v = something; } void g() { Optional!int v = someCalculation(); if (!isNull(v)) { try { f(v); } catch (SomeException) { report_error(v); // Oops, invalid: 'v' turned into null. } } }What is the point of this example? Use ref if you don't want f to change the value of it's argument, not out.Note how in the second example, the semantics of the function 'f' subtly change depending on whether the argument is an 'int' or and 'Optional!int'.Do they? I think the bug is that f's argument is out. What if the argument is int, you get report_error(0), which tells you nothing. in fact, you might as well just change the call to report_error(typeof(v).init);Proxies, especially proxies that add functionality to their base type, cannot act exactly like the type they are proxying. Using a proxy therefore requires the significant mental overhead of keeping track of all the corner cases in which the proxy does not act like the type it is proxying, as well as the hassle of working around those limitation whenever they come up.They can act like the base type up to a point. You make it sound like a chore to use a wrapper type, but the truth is they are easy to use, there are not that many cases to worry about. -Steve
Sep 10 2009
Steven Schveighoffer wrote:On Thu, 10 Sep 2009 17:41:19 -0400, Rainer Deyke <rainerd eldwood.com> wrote:OK, this is just an annoyance, not actual danger. It's still an inconsistency. If you had a way to get to the contained value (as an lvalue), you could pass this value to 'f' as a reference.And I see no value at all, but a lot of danger. Even ignoring, what about this: void f(ref int v) { // Do something... } void g() { Optional!int x = 5; f(x); }Doesn't compile. Implicit casting doesn't mean implicit reference casting.No, 'out' is correct. I want 'f' to return a value through its argument. If 'f' terminates through an exception before it calculates the new value of 'v', I still want it to clobber the previous value of 'v' in order to prevent the error from being masked.Or this: void report_error(int); void f(T)(out T v) { if (error_condition) throw new SomeException(); v = something; } void g() { Optional!int v = someCalculation(); if (!isNull(v)) { try { f(v); } catch (SomeException) { report_error(v); // Oops, invalid: 'v' turned into null. } } }What is the point of this example? Use ref if you don't want f to change the value of it's argument, not out.They absolutely do. You pass in an 'int', you get one result, you pass in an 'Optional!int', you get another result. Maybe the use of exceptions is confusing you. Here is another example: void defaultInitialize(T)(ref T) { T = T.init; } Again, one result if you pass in an 'int', another result if you pass in an 'Optional!T'. Logically the same operation, but physically different results.Note how in the second example, the semantics of the function 'f' subtly change depending on whether the argument is an 'int' or and 'Optional!int'.Do they?I think the bug is that f's argument is out. What if the argument is int, you get report_error(0), which tells you nothing. in fact, you might as well just change the call to report_error(typeof(v).init);That's another good example, actually. report_error(typeof(v).init); Legal when 'typeof(v)' is an int. Illegal if 'typeof(v) is 'Optional!int', because 'Optional!int' is not and cannot be fully equivalent to 'int' while also supporting null functionality. When you just see that one line of code, the problem is obvious. When it's buried in several layers of template code, then it's a lot less obvious.Up until you forget about one of those cases, at which you have a program that mysteriously fails to compile at best and an incorrect program at worst. -- Rainer Deyke - rainerd eldwood.comProxies, especially proxies that add functionality to their base type, cannot act exactly like the type they are proxying. Using a proxy therefore requires the significant mental overhead of keeping track of all the corner cases in which the proxy does not act like the type it is proxying, as well as the hassle of working around those limitation whenever they come up.They can act like the base type up to a point. You make it sound like a chore to use a wrapper type, but the truth is they are easy to use, there are not that many cases to worry about.
Sep 10 2009
On Fri, 11 Sep 2009 00:49:58 -0400, Rainer Deyke <rainerd eldwood.com> wrote:Just like defaultInitialize(double_value) is different than defaultInitialize(int_value). So what? I still don't get the problem. Optional!int is not an int. I never said it was.Do they?They absolutely do. You pass in an 'int', you get one result, you pass in an 'Optional!int', you get another result. Maybe the use of exceptions is confusing you. Here is another example: void defaultInitialize(T)(ref T) { T = T.init; } Again, one result if you pass in an 'int', another result if you pass in an 'Optional!T'. Logically the same operation, but physically different results.You didn't give me any details on report_error. If report_error takes an int, then yes, it fails at runtime, but that line of code is broken anyways. Why should report_error take ANY arguments when you always pass it an uninteresting value? If you always want to report the same thing, then use an int literal. Why do you think Optional!int has to act EXACTLY like an int? If it did THERE WOULD BE NO POINT!report_error(typeof(v).init);That's another good example, actually. report_error(typeof(v).init); Legal when 'typeof(v)' is an int. Illegal if 'typeof(v) is 'Optional!int', because 'Optional!int' is not and cannot be fully equivalent to 'int' while also supporting null functionality.When you just see that one line of code, the problem is obvious. When it's buried in several layers of template code, then it's a lot less obvious.Welcome to the world of programming, where logic errors are not obvious, I'm sorry the compiler can't flag all your errors for you. BTW, this example would behave no differently with your proposal. Amazingly even this compiles! int *ptr; report_error(*ptr);If you cannot deal with the cases where it's different, then don't use it. Use a struct with a boolean where the accesses to the object and the boolean are spelled out for you if you need that kind of assurance. It's not that interesting of a type, it doesn't belong in the standard library (well, maybe as the generic pair!(T, U) ). -SteveThey can act like the base type up to a point. You make it sound like a chore to use a wrapper type, but the truth is they are easy to use, there are not that many cases to worry about.Up until you forget about one of those cases, at which you have a program that mysteriously fails to compile at best and an incorrect program at worst.
Sep 11 2009
Steven Schveighoffer wrote:On Thu, 10 Sep 2009 17:41:19 -0400, Rainer Deyke <rainerd eldwood.com> wrote:My experience with wrappers in C++ has been similar to Rainer's. Essentially you can't define "smart references" in C++. I hope we were or will be able to fix that with the "alias this" feature. AndreiProxies, especially proxies that add functionality to their base type, cannot act exactly like the type they are proxying. Using a proxy therefore requires the significant mental overhead of keeping track of all the corner cases in which the proxy does not act like the type it is proxying, as well as the hassle of working around those limitation whenever they come up.They can act like the base type up to a point. You make it sound like a chore to use a wrapper type, but the truth is they are easy to use, there are not that many cases to worry about.
Sep 11 2009
On Fri, 11 Sep 2009 11:20:03 -0400, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:Steven Schveighoffer wrote:My experience with Proxy objects in .Net remoting has been good. Granted, it's not a true wrapper, since the object has to derive from a certain base class, but it works very seamlessly without much effort. I guess it's a matter of how much you expect from your wrapper. I also agree that C++ doesn't allow complete smart references, but we aren't dealing with C++ here, are we :) -SteveOn Thu, 10 Sep 2009 17:41:19 -0400, Rainer Deyke <rainerd eldwood.com> wrote:My experience with wrappers in C++ has been similar to Rainer's. Essentially you can't define "smart references" in C++. I hope we were or will be able to fix that with the "alias this" feature.Proxies, especially proxies that add functionality to their base type, cannot act exactly like the type they are proxying. Using a proxy therefore requires the significant mental overhead of keeping track of all the corner cases in which the proxy does not act like the type it is proxying, as well as the hassle of working around those limitation whenever they come up.They can act like the base type up to a point. You make it sound like a chore to use a wrapper type, but the truth is they are easy to use, there are not that many cases to worry about.
Sep 11 2009
Rainer Deyke wrote:Steven Schveighoffer wrote:The value is in the fact that you can use an Optional!T as a substitute for a T. Actually that's quite what happens today with null references - we know they can't be used, but they are substitutable for valid objects.Sure, do whatever you want for your *private data members*. I see too much value in Optional!T being aliased to the underlying T to go for the completely generic solution which requires special syntax to get at the T.And I see no value at all, but a lot of danger.Even ignoring, what about this: void f(ref int v) { // Do something... } void g() { Optional!int x = 5; f(x); }Such code should not compile. An Optional!T should be at most substitutable for an rvalue of type T.Or this: void report_error(int); void f(T)(out T v) { if (error_condition) throw new SomeException(); v = something; } void g() { Optional!int v = someCalculation(); if (!isNull(v)) { try { f(v); } catch (SomeException) { report_error(v); // Oops, invalid: 'v' turned into null. } } } Note how in the second example, the semantics of the function 'f' subtly change depending on whether the argument is an 'int' or and 'Optional!int'.If it's an int, you assign the int. If it's an Optional!int, you assign the optional, which assigns the int and makes the Optional non-null if it was null. I might be missing something, but I don't think there's much danger for confusion here.Proxies, especially proxies that add functionality to their base type, cannot act exactly like the type they are proxying. Using a proxy therefore requires the significant mental overhead of keeping track of all the corner cases in which the proxy does not act like the type it is proxying, as well as the hassle of working around those limitation whenever they come up.I agree to some extent. Andrei
Sep 11 2009
Andrei Alexandrescu wrote:Boost's Optional). Apparently a good design is to define Optional!T with a minimum of member functions (ideally none) and have it use the "alias this" feature to masquerade as a T. That way Optional!T looks and feels much like a T, except that it supports a function bool isNull(T)(Optional!T value); Am I on the right track? If so, what is the name you'd prefer for this artifact? AndreiI prefer Nullable. Will there be an isNull overload for types that are inherently nullable (class references, pointers, delegates, etc)? Probably, there should be an isNullable concept for checking any type for nullability along with a ValueOfNullable template for obtaining the type of the underlying value. Should we be able to generically construct a Nullable!T from a value of T? How to generically set a nullable to null? Do we need a requirement that assert(isNull(Nullable!(T).init)) pass for any T? Can 'to' be extended to correctly convert nullable types from and to "null" string? Should conversions from "null" be case-sensitive?
Sep 03 2009
Andrei Alexandrescu, el 2 de septiembre a las 14:39 me escribiste:Optional). Apparently a good design is to define Optional!T with a minimum of member functions (ideally none) and have it use the "alias this" feature to masquerade as a T. That way Optional!T looks and feels much like a T, except that it supports a function bool isNull(T)(Optional!T value); Am I on the right track? If so, what is the name you'd prefer for this artifact?Maybe this is a very silly question, but what is exactly the difference between Optional!T o/isNull(o) and T* o/o is null? -- Leandro Lucarella (luca) | Blog colectivo: http://www.mazziblog.com.ar/blog/ ---------------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------------- "Somos testigos de Jaimito, venimos a traer la salvación, el mundo va a desaparecer, somos testigos de Jaimito!". Nos enyoguizamos... Así que "somos testigos"? Te dejo el culo hecho un vino, y la conch'itumá, y la conch'itumá! -- Sidharta Kiwi
Sep 03 2009
On Thu, 03 Sep 2009 10:40:13 -0400, Leandro Lucarella <llucax gmail.com> wrote:Andrei Alexandrescu, el 2 de septiembre a las 14:39 me escribiste:The difference is you don't have to store the T somewhere else. That is, an Optional!T contains both the value if it is not null AND whether it is null or not. With a T*, the value is stored elsewhere, and you may have aliasing problems. -SteveBoost's Optional). Apparently a good design is to define Optional!T with a minimum of member functions (ideally none) and have it use the "alias this" feature to masquerade as a T. That way Optional!T looks and feels much like a T, except that it supports a function bool isNull(T)(Optional!T value); Am I on the right track? If so, what is the name you'd prefer for this artifact?Maybe this is a very silly question, but what is exactly the difference between Optional!T o/isNull(o) and T* o/o is null?
Sep 03 2009
Steven Schveighoffer wrote:On Thu, 03 Sep 2009 10:40:13 -0400, Leandro Lucarella <llucax gmail.com> wrote:Oh, that I forgot and is essential! A closer question is what's the difference between OptionalRef!T and T*. That difference is smaller because OptionalRef!T actually stores exactly a T* inside. AndreiAndrei Alexandrescu, el 2 de septiembre a las 14:39 me escribiste:The difference is you don't have to store the T somewhere else. That is, an Optional!T contains both the value if it is not null AND whether it is null or not. With a T*, the value is stored elsewhere, and you may have aliasing problems. -SteveBoost's Optional). Apparently a good design is to define Optional!T with a minimum of member functions (ideally none) and have it use the "alias this" feature to masquerade as a T. That way Optional!T looks and feels much like a T, except that it supports a function bool isNull(T)(Optional!T value); Am I on the right track? If so, what is the name you'd prefer for this artifact?Maybe this is a very silly question, but what is exactly the difference between Optional!T o/isNull(o) and T* o/o is null?
Sep 03 2009
Steven Schveighoffer, el 3 de septiembre a las 11:22 me escribiste:On Thu, 03 Sep 2009 10:40:13 -0400, Leandro Lucarella <llucax gmail.com> wrote:That doesn't seems like a big problem having a GC.Andrei Alexandrescu, el 2 de septiembre a las 14:39 me escribiste:The difference is you don't have to store the T somewhere else.Optional). Apparently a good design is to define Optional!T with a minimum of member functions (ideally none) and have it use the "alias this" feature to masquerade as a T. That way Optional!T looks and feels much like a T, except that it supports a function bool isNull(T)(Optional!T value); Am I on the right track? If so, what is the name you'd prefer for this artifact?Maybe this is a very silly question, but what is exactly the difference between Optional!T o/isNull(o) and T* o/o is null?That is, an Optional!T contains both the value if it is not null AND whether it is null or not. With a T*, the value is stored elsewhere, and you may have aliasing problems.Ok, this seems like a reasonable point. So Optional!T is a value type, right? -- Leandro Lucarella (luca) | Blog colectivo: http://www.mazziblog.com.ar/blog/ ---------------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------------- CONDUCTOR BORRACHO CASI PROVOCA UNA TRAGEDIA: BATMAN UNICO TESTIGO -- Crónica TV
Sep 03 2009
On Thu, 03 Sep 2009 11:50:55 -0400, Leandro Lucarella <llucax gmail.com> wrote:Steven Schveighoffer, el 3 de septiembre a las 11:22 me escribiste:On Thu, 03 Sep 2009 10:40:13 -0400, Leandro LucarellaThere are performance concerns, you need 16 bytes minimum to store a value on the heap, so a 4-byte integer becomes 16 bytes on the heap. And the GC is way slower than storing on the stack.That doesn't seems like a big problem having a GC.Maybe this is a very silly question, but what is exactly the difference between Optional!T o/isNull(o) and T* o/o is null?The difference is you don't have to store the T somewhere else.If T is a value type, yes. I'm not sure what Andrei has in mind if T is a reference type. -SteveThat is, an Optional!T contains both the value if it is not null AND whether it is null or not. With a T*, the value is stored elsewhere, and you may have aliasing problems.Ok, this seems like a reasonable point. So Optional!T is a value type, right?
Sep 03 2009
Leandro Lucarella wrote:Andrei Alexandrescu, el 2 de septiembre a las 14:39 me escribiste:It's a good question. There are two differences: (a) Optional!T behaves almost like a T in expressions, and (b) Optional!T does not support most features of pointers, such as arithmetic and comparison. AndreiOptional). Apparently a good design is to define Optional!T with a minimum of member functions (ideally none) and have it use the "alias this" feature to masquerade as a T. That way Optional!T looks and feels much like a T, except that it supports a function bool isNull(T)(Optional!T value); Am I on the right track? If so, what is the name you'd prefer for this artifact?Maybe this is a very silly question, but what is exactly the difference between Optional!T o/isNull(o) and T* o/o is null?
Sep 03 2009
Andrei Alexandrescu wrote:Leandro Lucarella wrote:The answer above is incorrect. I actually answered about OptionalRef!T. Steve's answer is correct. AndreiAndrei Alexandrescu, el 2 de septiembre a las 14:39 me escribiste:It's a good question. There are two differences: (a) Optional!T behaves almost like a T in expressions, and (b) Optional!T does not support most features of pointers, such as arithmetic and comparison. AndreiBoost's Optional). Apparently a good design is to define Optional!T with a minimum of member functions (ideally none) and have it use the "alias this" feature to masquerade as a T. That way Optional!T looks and feels much like a T, except that it supports a function bool isNull(T)(Optional!T value); Am I on the right track? If so, what is the name you'd prefer for this artifact?Maybe this is a very silly question, but what is exactly the difference between Optional!T o/isNull(o) and T* o/o is null?
Sep 03 2009
Andrei Alexandrescu, el 3 de septiembre a las 10:34 me escribiste:Leandro Lucarella wrote:(a) And *o behaves *exactly* like T, that doesn't seems like an advantage So, judging for (b) I guess is just a safety meassure. I agree with some other mail in this thread that it would be better to have proper references if you don't want to allow pointer arithmetics... -- Leandro Lucarella (luca) | Blog colectivo: http://www.mazziblog.com.ar/blog/ ---------------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------------- Hey you, dont help them to bury the light Don't give in without a fight.Andrei Alexandrescu, el 2 de septiembre a las 14:39 me escribiste:It's a good question. There are two differences: (a) Optional!T behaves almost like a T in expressions, and (b) Optional!T does not support most features of pointers, such as arithmetic and comparison.Optional). Apparently a good design is to define Optional!T with a minimum of member functions (ideally none) and have it use the "alias this" feature to masquerade as a T. That way Optional!T looks and feels much like a T, except that it supports a function bool isNull(T)(Optional!T value); Am I on the right track? If so, what is the name you'd prefer for this artifact?Maybe this is a very silly question, but what is exactly the difference between Optional!T o/isNull(o) and T* o/o is null?
Sep 03 2009
On Wed, 02 Sep 2009 15:39:28 -0400, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:Boost's Optional). Apparently a good design is to define Optional!T with a minimum of member functions (ideally none) and have it use the "alias this" feature to masquerade as a T. That way Optional!T looks and feels much like a T, except that it supports a function bool isNull(T)(Optional!T value); Am I on the right track? If so, what is the name you'd prefer for this artifact?I like nullable, but optional will work. --- I noticed that in some of your responses, you refer to OptionalRef, Can says that you can't apply the Nullable struct to a non-value type (because reference types are nullable). was assuming that Optional!T would simply alias to T if T is a reference type already (such as a class or pointer). This is also going to be better looking if the compiler helps. Having to do isNull(x) all the time instead of x !is null is going to be a pain when you aren't sure whether x is an Optional!T or a true reference type (such as a class). It would also solve some other problems. For instance being able to test if Rebindable is null... I wonder if the compiler could do something nifty like this: struct S { int n; alias n this; static S opNull() { return S(int.min); } } S s; s = null; // translates to s = S.opNull(); if(s !is null); // translates to if(s !is S.opNull()) Now you have a "nullable" int type that works for most cases and only requires an int storage space. I don't understand the ramifications on the context-free grammar, but does something like this seem feasible? -Steve
Sep 03 2009
On Thu, Sep 3, 2009 at 12:02 PM, Steven Schveighoffer<schveiguy yahoo.com> wrote:On Wed, 02 Sep 2009 15:39:28 -0400, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:srOptional). Apparently a good design is to define Optional!T with a minimum of membe=ceptfunctions (ideally none) and have it use the "alias this" feature to masquerade as a T. That way Optional!T looks and feels much like a T, ex=outhat it supports a function bool isNull(T)(Optional!T value); Am I on the right track? If so, what is the name you'd prefer for this artifact?I like nullable, but optional will work. --- I noticed that in some of your responses, you refer to OptionalRef, Can y=ysthat you can't apply the Nullable struct to a non-value type (because reference types are nullable).Iwas assuming that Optional!T would simply alias to T if T is a reference type already (such as a class or pointer). This is also going to be better looking if the compiler helps. =A0Having =to doisNull(x) all the time instead of x !is null is going to be a pain when y=ouaren't sure whether x is an Optional!T or a true reference type (such as =aclass). It would also solve some other problems. =A0For instance being able to te=st ifRebindable is null... I wonder if the compiler could do something nifty like this: struct S { =A0int n; =A0alias n this; =A0static S opNull() { return S(int.min); } } S s; s =3D null; // translates to s =3D S.opNull(); if(s !is null); // translates to if(s !is S.opNull()) Now you have a "nullable" int type that works for most cases and only requires an int storage space. I don't understand the ramifications on the context-free grammar, but doe=ssomething like this seem feasible?The semantics here have nothing to do with the grammar. It's not really that different from operator overloading.
Sep 03 2009
On Wed, 02 Sep 2009 15:39:28 -0400, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:Boost's Optional). Apparently a good design is to define Optional!T with a minimum of member functions (ideally none) and have it use the "alias this" feature to masquerade as a T. That way Optional!T looks and feels much like a T, except that it supports a function bool isNull(T)(Optional!T value); Am I on the right track? If so, what is the name you'd prefer for this artifact?I just thought of something else. If you use alias this, then what happens here? Optional!int i = null; int n = i; If you are counting on i to implicitly cast to the int using alias this, then this probably won't work right (I would think it should throw or require an explicit cast). I guess if you alias this to a function (is that possible yet?) which checks for null first, then you may have better behavior, but you still should probably require a cast. -Steve
Sep 03 2009
On Wed, 02 Sep 2009 23:39:28 +0400, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:Boost's Optional). Apparently a good design is to define Optional!T with a minimum of member functions (ideally none) and have it use the "alias this" feature to masquerade as a T. That way Optional!T looks and feels much like a T, except that it supports a function bool isNull(T)(Optional!T value); Am I on the right track? If so, what is the name you'd prefer for this artifact? AndreiNullable, unless this feature is planned for value types only (please, no!)
Sep 03 2009
Andrei Alexandrescu wrote:Boost's Optional). Apparently a good design is to define Optional!T with a minimum of member functions (ideally none) and have it use the "alias this" feature to masquerade as a T. That way Optional!T looks and feels much like a T, except that it supports a function bool isNull(T)(Optional!T value); Am I on the right track? If so, what is the name you'd prefer for this artifact? AndreiGiven the apocalyptic battle raging in this thread at the moment, maybe it would be pertinent to have both. Sometimes, you do want a generic mechanism for representing "this may or may not have a value". Other times, you just want something with a "no value set" sentinel. Nullable!T would be a type for which 'null' is a valid value. By this definition, Nullable!(Nullable!T) is obviously equivalent to Nullable!T; it's equivalent to T if T is either a pointer, array or object. Optional!T would be a type where an instance may or may not have a value of type T; even if T is itself Optional. I'm tempted to put an alias this into Optional and just define an external contentsOf function that pulls out the value contained in the Optional. Not sure if that's the right choice or not. So that gives us: struct Nullable(T) { private { T __value; bool __isNull; T __get_value() { if( __isNull ) throw new Exception; return __value; } } alias __get_value this; } struct Optional(T) { private { T __value; bool __isNull; T __get_value() { if( __isNull ) throw new Exception; return __value; } } // Maybe: alias __get_value this; alias __get_value opCall; bool isNull() { return __isNull; } } static assert( is( Nullable!(Nullable!int) == Nullable!int ) ); bool isNull(T)(T v) if (isNullable!T) { return v.__isNull; } bool isNull(T)(T v) if (isOptional!T) { return v.isNull; } /+ Maybe: T contentsOf(T)(T v) if (isNullable!T || isOptional!T) { return v.__get_value; } +/
Sep 08 2009
Daniel Keep wrote:Andrei Alexandrescu wrote:[snip] Thanks. I think what I'll do for now is define Optional and see where things go. AndreiBoost's Optional). Apparently a good design is to define Optional!T with a minimum of member functions (ideally none) and have it use the "alias this" feature to masquerade as a T. That way Optional!T looks and feels much like a T, except that it supports a function bool isNull(T)(Optional!T value); Am I on the right track? If so, what is the name you'd prefer for this artifact? AndreiGiven the apocalyptic battle raging in this thread at the moment, maybe it would be pertinent to have both. Sometimes, you do want a generic mechanism for representing "this may or may not have a value". Other times, you just want something with a "no value set" sentinel. Nullable!T would be a type for which 'null' is a valid value. By this definition, Nullable!(Nullable!T) is obviously equivalent to Nullable!T; it's equivalent to T if T is either a pointer, array or object. Optional!T would be a type where an instance may or may not have a value of type T; even if T is itself Optional. I'm tempted to put an alias this into Optional and just define an external contentsOf function that pulls out the value contained in the Optional. Not sure if that's the right choice or not. So that gives us:
Sep 09 2009