www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Nullable or Optional? Or something else?

reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
I plan to add a Nullable struct to Phobos (akin to C#'s Nullable, 
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
next sibling parent reply "Danny Wilson" <bluezenix gmail.com> writes:
Op Wed, 02 Sep 2009 21:39:28 +0200 schreef Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org>:

 I plan to add a Nullable struct to Phobos (akin to C#'s Nullable,  
 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

How about: Maybe!T Got that from Haskell :-)
Sep 02 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Jarrett Billingsley wrote:
 On Wed, Sep 2, 2009 at 4:13 PM, Simen Kjaeraas<simen.kjaras gmail.com> wrote:
 Danny Wilson <bluezenix gmail.com> wrote:

 Op Wed, 02 Sep 2009 21:39:28 +0200 schreef Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org>:

 I plan to add a Nullable struct to Phobos (akin to C#'s Nullable, 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

How about: Maybe!T Got that from Haskell :-)


Thirded!

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. Andrei
Sep 02 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Danny Wilson wrote:
 Op Wed, 02 Sep 2009 22:20:04 +0200 schreef Andrei Alexandrescu 
 <SeeWebsiteForEmail erdani.org>:
 
 Jarrett Billingsley wrote:
 On Wed, Sep 2, 2009 at 4:13 PM, Simen 
 Kjaeraas<simen.kjaras gmail.com> wrote:
 Danny Wilson <bluezenix gmail.com> wrote:

 Op Wed, 02 Sep 2009 21:39:28 +0200 schreef Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org>:

 I plan to add a Nullable struct to Phobos (akin to C#'s Nullable, 
 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

How about: Maybe!T Got that from Haskell :-)



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. Andrei

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
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Danny Wilson wrote:
 Op Wed, 02 Sep 2009 22:55:53 +0200 schreef Andrei Alexandrescu 
 <SeeWebsiteForEmail erdani.org>:
 
  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

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) ?

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.) Andrei
Sep 02 2009
prev sibling next sibling parent "Simen Kjaeraas" <simen.kjaras gmail.com> writes:
Danny Wilson <bluezenix gmail.com> wrote:

 Op Wed, 02 Sep 2009 21:39:28 +0200 schreef Andrei Alexandrescu  
 <SeeWebsiteForEmail erdani.org>:

 I plan to add a Nullable struct to Phobos (akin to C#'s Nullable,  
 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

How about: Maybe!T Got that from Haskell :-)

I also feel the bikeshed should be colored 'Maybe'. -- Simen
Sep 02 2009
prev sibling next sibling parent Jarrett Billingsley <jarrett.billingsley gmail.com> writes:
On Wed, Sep 2, 2009 at 4:13 PM, Simen Kjaeraas<simen.kjaras gmail.com> wrot=
e:
 Danny Wilson <bluezenix gmail.com> wrote:

 Op Wed, 02 Sep 2009 21:39:28 +0200 schreef Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org>:

 I plan to add a Nullable struct to Phobos (akin to C#'s Nullable, Boost=



 Optional).

 Apparently a good design is to define Optional!T with a minimum of memb=



 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, e=



 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

How about: =A0Maybe!T Got that from Haskell :-)

I also feel the bikeshed should be colored 'Maybe'.

Thirded!
Sep 02 2009
prev sibling next sibling parent reply grauzone <none example.net> writes:
Andrei Alexandrescu wrote:
 I plan to add a Nullable struct to Phobos (akin to C#'s Nullable, 
 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

I 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
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
grauzone wrote:
 Andrei Alexandrescu wrote:
 I plan to add a Nullable struct to Phobos (akin to C#'s Nullable, 
 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

I still don't understand how one can feel comfortable with the fact, that "alias this" can overshadow arbitrary members of the alias'ed type.

That's why I want to add no member functions to Optional. The test for null will be a free function. Andrei
Sep 02 2009
next sibling parent Rainer Deyke <rainerd eldwood.com> writes:
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
prev sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Steven Schveighoffer wrote:
 On Wed, 02 Sep 2009 16:54:30 -0400, Andrei Alexandrescu 
 <SeeWebsiteForEmail erdani.org> wrote:
 
 grauzone wrote:
 Andrei Alexandrescu wrote:
 I plan to add a Nullable struct to Phobos (akin to C#'s Nullable, 
 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

that "alias this" can overshadow arbitrary members of the alias'ed type.

That's why I want to add no member functions to Optional. The test for null will be a free function.

How does Optional!valuetype support this: Optional!valuetype x; x = null; Don't you need opAssign? -Steve

I should have said: no *named* member. Operators and cdtors are fair game. Andrei
Sep 03 2009
prev sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Wed, 02 Sep 2009 16:54:30 -0400, Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org> wrote:

 grauzone wrote:
 Andrei Alexandrescu wrote:
 I plan to add a Nullable struct to Phobos (akin to C#'s Nullable,  
 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

that "alias this" can overshadow arbitrary members of the alias'ed type.

That's why I want to add no member functions to Optional. The test for null will be a free function.

How does Optional!valuetype support this: Optional!valuetype x; x = null; Don't you need opAssign? -Steve
Sep 03 2009
prev sibling next sibling parent "Danny Wilson" <bluezenix gmail.com> writes:
Op Wed, 02 Sep 2009 22:20:04 +0200 schreef Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org>:

 Jarrett Billingsley wrote:
 On Wed, Sep 2, 2009 at 4:13 PM, Simen Kjaeraas<simen.kjaras gmail.com>  
 wrote:
 Danny Wilson <bluezenix gmail.com> wrote:

 Op Wed, 02 Sep 2009 21:39:28 +0200 schreef Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org>:

 I plan to add a Nullable struct to Phobos (akin to C#'s Nullable,  
 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

How about: Maybe!T Got that from Haskell :-)



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. Andrei

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?
Sep 02 2009
prev sibling next sibling parent "Simen Kjaeraas" <simen.kjaras gmail.com> writes:
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
prev sibling next sibling parent Jarrett Billingsley <jarrett.billingsley gmail.com> writes:
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
prev sibling next sibling parent "Danny Wilson" <bluezenix gmail.com> writes:
Op Wed, 02 Sep 2009 22:55:53 +0200 schreef Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org>:

  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

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) ?
Sep 02 2009
prev sibling next sibling parent bearophile <bearophileHUGS lycos.com> writes:
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
prev sibling next sibling parent reply Jeremie Pelletier <jeremiep gmail.com> writes:
Andrei Alexandrescu Wrote:

 I plan to add a Nullable struct to Phobos (akin to C#'s Nullable, 
 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

I 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
parent Jarrett Billingsley <jarrett.billingsley gmail.com> writes:
On Wed, Sep 2, 2009 at 8:20 PM, Jeremie Pelletier<jeremiep gmail.com> wrote:
 Andrei Alexandrescu Wrote:

 I plan to add a Nullable struct to Phobos (akin to C#'s Nullable,
 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

I 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.

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, huh
Sep 02 2009
prev sibling next sibling parent reply Rainer Deyke <rainerd eldwood.com> writes:
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
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Rainer Deyke wrote:
 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.

Interesting. I wonder whether it's better to fold Optional!(Optional!T) into Optional!T. Andrei
Sep 02 2009
parent reply Rainer Deyke <rainerd eldwood.com> writes:
Andrei Alexandrescu wrote:
 Rainer Deyke wrote:
 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.

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.com
Sep 02 2009
parent reply Rainer Deyke <rainerd eldwood.com> writes:
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
parent reply Rainer Deyke <rainerd eldwood.com> writes:
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
parent reply Rainer Deyke <rainerd eldwood.com> writes:
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.
 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.

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.com
Sep 08 2009
parent reply Rainer Deyke <rainerd eldwood.com> writes:
Steven Schveighoffer wrote:
 On Tue, 08 Sep 2009 15:50:15 -0400, Rainer Deyke <rainerd eldwood.com>
 wrote:
 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?

New example: struct AlgebraicFunction { double getMaximum() { if (isNull(this.cachedMaximum)) { this.cachedMaximum = this.veryExpensiveCalculation(); } return *this.cachedMaximum; } private Optional!(Optional!double) cached_maximum; }
 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.
 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

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.com
Sep 08 2009
next sibling parent Rainer Deyke <rainerd eldwood.com> writes:
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
prev sibling parent reply Rainer Deyke <rainerd eldwood.com> writes:
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
parent reply Rainer Deyke <rainerd eldwood.com> writes:
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
parent reply Rainer Deyke <rainerd eldwood.com> writes:
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
next sibling parent Rainer Deyke <rainerd eldwood.com> writes:
Steven Schveighoffer wrote:
 On Thu, 10 Sep 2009 17:41:19 -0400, Rainer Deyke <rainerd eldwood.com>
 wrote:
 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.

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.
 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.

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.
 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?

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.
  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.
 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.

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.com
Sep 10 2009
prev sibling next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Rainer Deyke 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.

And I see no value at all, but a lot of danger.

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.
  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
prev sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Steven Schveighoffer wrote:
 On Thu, 10 Sep 2009 17:41:19 -0400, Rainer Deyke <rainerd eldwood.com> 
 wrote:
 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.

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. Andrei
Sep 11 2009
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 03 Sep 2009 02:36:12 -0400, Rainer Deyke <rainerd eldwood.com>  
wrote:

 Andrei Alexandrescu wrote:
 Rainer Deyke wrote:
 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.

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.

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. -Steve
Sep 03 2009
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 03 Sep 2009 16:20:41 -0400, Rainer Deyke <rainerd eldwood.com>  
wrote:

 Steven Schveighoffer wrote:
 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.

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. -Steve
Sep 04 2009
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Fri, 04 Sep 2009 22:45:40 -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.

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?

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 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.

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. -Steve
Sep 08 2009
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Tue, 08 Sep 2009 15:50:15 -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.

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?
 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.
  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?

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.
 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.

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 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". -Steve
Sep 08 2009
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Tue, 08 Sep 2009 19:24:31 -0400, Rainer Deyke <rainerd eldwood.com>  
wrote:

 Steven Schveighoffer wrote:
 On Tue, 08 Sep 2009 15:50:15 -0400, Rainer Deyke <rainerd eldwood.com>
 wrote:
 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?

New example: struct AlgebraicFunction { double getMaximum() { if (isNull(this.cachedMaximum)) { this.cachedMaximum = this.veryExpensiveCalculation(); } return *this.cachedMaximum; } private Optional!(Optional!double) cached_maximum; }

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; }
 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.

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 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.

Here we simply disagree.
 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

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'.

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. -Steve
Sep 10 2009
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 10 Sep 2009 12:47:35 -0400, Rainer Deyke <rainerd eldwood.com>  
wrote:

    }

    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.

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
Sep 10 2009
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 10 Sep 2009 14:11:55 -0400, Rainer Deyke <rainerd eldwood.com>  
wrote:

 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.

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. -Steve
Sep 10 2009
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 10 Sep 2009 17:41:19 -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.

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.
 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
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Fri, 11 Sep 2009 00:49:58 -0400, Rainer Deyke <rainerd eldwood.com>  
wrote:

 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.

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.
 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.

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!
 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);
 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.

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.

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) ). -Steve
Sep 11 2009
prev sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Fri, 11 Sep 2009 11:20:03 -0400, Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org> wrote:

 Steven Schveighoffer wrote:
 On Thu, 10 Sep 2009 17:41:19 -0400, Rainer Deyke <rainerd eldwood.com>  
 wrote:
 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.

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.

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.

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 :) -Steve
Sep 11 2009
prev sibling next sibling parent Max Samukha <spambox d-coding.com> writes:
Andrei Alexandrescu wrote:

 I plan to add a Nullable struct to Phobos (akin to C#'s Nullable,
 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

I 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
prev sibling next sibling parent reply Leandro Lucarella <llucax gmail.com> writes:
Andrei Alexandrescu, el  2 de septiembre a las 14:39 me escribiste:
 I plan to add a Nullable struct to Phobos (akin to C#'s Nullable, 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?

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
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Leandro Lucarella wrote:
 Andrei Alexandrescu, el  2 de septiembre a las 14:39 me escribiste:
 I plan to add a Nullable struct to Phobos (akin to C#'s Nullable, 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?

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?

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. Andrei
Sep 03 2009
next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Andrei Alexandrescu wrote:
 Leandro Lucarella wrote:
 Andrei Alexandrescu, el  2 de septiembre a las 14:39 me escribiste:
 I plan to add a Nullable struct to Phobos (akin to C#'s Nullable, 
 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?

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?

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. Andrei

The answer above is incorrect. I actually answered about OptionalRef!T. Steve's answer is correct. Andrei
Sep 03 2009
prev sibling parent Leandro Lucarella <llucax gmail.com> writes:
Andrei Alexandrescu, el  3 de septiembre a las 10:34 me escribiste:
 Leandro Lucarella wrote:
Andrei Alexandrescu, el  2 de septiembre a las 14:39 me escribiste:
I plan to add a Nullable struct to Phobos (akin to C#'s Nullable, 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?

between Optional!T o/isNull(o) and T* o/o is null?

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.

(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.
Sep 03 2009
prev sibling next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Steven Schveighoffer wrote:
 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:
 I plan to add a Nullable struct to Phobos (akin to C#'s Nullable, 
 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?

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. 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. -Steve

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. Andrei
Sep 03 2009
prev sibling parent Leandro Lucarella <llucax gmail.com> writes:
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:
 
Andrei Alexandrescu, el  2 de septiembre a las 14:39 me escribiste:
I plan to add a Nullable struct to Phobos (akin to C#'s Nullable, 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?

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.

That doesn't seems like a big problem having a GC.
 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
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
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:
 I plan to add a Nullable struct to Phobos (akin to C#'s Nullable,  
 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?

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. 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. -Steve
Sep 03 2009
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Wed, 02 Sep 2009 15:39:28 -0400, Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org> wrote:

 I plan to add a Nullable struct to Phobos (akin to C#'s Nullable,  
 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 you elaborate on that? I looked up Nullable for C#, and it specifically says that you can't apply the Nullable struct to a non-value type (because reference types are nullable). Since we are in D and not C# and have vastly better template mechanisms, I 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
prev sibling next sibling parent Jarrett Billingsley <jarrett.billingsley gmail.com> writes:
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:

 I plan to add a Nullable struct to Phobos (akin to C#'s Nullable, Boost'=


 Optional).

 Apparently a good design is to define Optional!T with a minimum of membe=


 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, ex=


 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 y=

 elaborate on that? =A0I looked up Nullable for C#, and it specifically sa=

 that you can't apply the Nullable struct to a non-value type (because
 reference types are nullable).

 Since we are in D and not C# and have vastly better template mechanisms, =

 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. =A0Having =

 isNull(x) all the time instead of x !is null is going to be a pain when y=

 aren't sure whether x is an Optional!T or a true reference type (such as =

 class).

 It would also solve some other problems. =A0For instance being able to te=

 Rebindable 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=

 something 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
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
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 Lucarella


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.

That doesn't seems like a big problem having a GC.

There 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 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?

If T is a value type, yes. I'm not sure what Andrei has in mind if T is a reference type. -Steve
Sep 03 2009
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Wed, 02 Sep 2009 15:39:28 -0400, Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org> wrote:

 I plan to add a Nullable struct to Phobos (akin to C#'s Nullable,  
 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
prev sibling next sibling parent "Denis Koroskin" <2korden gmail.com> writes:
On Wed, 02 Sep 2009 23:39:28 +0400, Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org> wrote:

 I plan to add a Nullable struct to Phobos (akin to C#'s Nullable,  
 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

Nullable, unless this feature is planned for value types only (please, no!)
Sep 03 2009
prev sibling parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
Andrei Alexandrescu wrote:
 I plan to add a Nullable struct to Phobos (akin to C#'s Nullable,
 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

Given 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
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Daniel Keep wrote:
 Andrei Alexandrescu wrote:
 I plan to add a Nullable struct to Phobos (akin to C#'s Nullable,
 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

Given 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:

Thanks. I think what I'll do for now is define Optional and see where things go. Andrei
Sep 09 2009