February 03, 2014
On Monday, 3 February 2014 at 14:21:29 UTC, Dicebot wrote:
> On Monday, 3 February 2014 at 14:17:11 UTC, Chris wrote:
>> Probably. I tried using Nullable, but it caused some problems when the attribute wasn't defined:
>>
>> core.exception.AssertError@/usr/include/dmd/phobos/std/typecons.d(1233): Called `get' on null Nullable!int.
>
> This is intended. The very point of Nullable is to force you to handle `null` state before accessing actual payload.

Aha, I see. So in my scenario it is useless.
February 03, 2014
On Monday, 3 February 2014 at 14:21:29 UTC, Dicebot wrote:
> This is intended. The very point of Nullable is to force you to handle `null` state before accessing actual payload.

P.S. for this very reason in my own implementation of Optional I provide only delegate access method:

value.get(
    ()    => { /* handle 'empty' case */ },
    value => { /* 'value' is legit data */ }
);
February 04, 2014
On Monday, 3 February 2014 at 10:25:19 UTC, Chris wrote:
> Is there a way I can make the return type in getAttribute generic? null does not work with numbers.
>
> MyStruct(T) {
>   T[T] attributes;
>   // ....
>   public auto getAttribute(T attr) {
>       if (!(attr in attributes)) {
>         return null; // Doesn't work for numbers!
>       }
>       return attributes[attr];
>     }
> }
>
> void main() {
>   auto myStr = MyStruct!int(0); // Error
> }

Whenever i am faced with this situation i do one (or more then one) of the following things.

struct MyStruct(T)
{
    T[T] attributes;

   //(1) Forward the underlying access method Eg:
   auto opBinaryRight(string s : "in")(T attrib)
   {
      return attrib in attributes;
   }

   //(2) make a try method.
   bool tryAttrib(T attrib, out T outAttrib)
   {
       auto p = attrib in attributes;
       if(p) outAttrib = *p;
       return p !is null;
   }



   //(3) Give user option to set default value.
   T attribOrDefault(T attrib, T default)
   {
       auto p = attrib im attributes;
       return p is null ? default : attrib;
   }


   //(4) Use Nullable!T (I prefer #5 over this one)
   Nullable!T attribOrNull(T attrib)
   {
       Nullable!T result;
       auto p = attrib ib attributes;
       if(p) result = *p;
       return result;
   }

   //(5) Use a pointer but not forward in operator.
   T* attribPtr(T attrib)
   {
      return attrib in attributes;
   }

   //(6) Throw exception (I only do this in combination with one of the above)
   T attribEx(T attrib)
   {
     return *enforce!AttribNotFoundEx(attrib in attributes);
   }
}

My personal preference using #2 and #3 in combination. #2 covers the basic case "Is this thing avalible?" and #3 covers the case "Give it to me if it is avalible or use this default value" I think it gives a clear image of what your code is doing at the callsite. Only using #2 or #3 limits you in this sence.

For #1, #4 and #5 i personally stay away from them. They force the caller to either use an if or potentially trigger a null pointer derecerence. (Btw what is the benefit of #4? I have never used it since it seems pointless)

I very rarly use attribEx. I don't think code shuld just spew exceptions all over the place. They should be reserved for really bad stuff, like bounds checks. One exception i make to this rule is if i'm dealing with ranges. Since the other methods don't lend themselfs for UFCS-chaing.


February 04, 2014
On Tuesday, 4 February 2014 at 00:43:54 UTC, TheFlyingFiddle wrote:
> On Monday, 3 February 2014 at 10:25:19 UTC, Chris wrote:
>> Is there a way I can make the return type in getAttribute generic? null does not work with numbers.
>>
>> MyStruct(T) {
>>  T[T] attributes;
>>  // ....
>>  public auto getAttribute(T attr) {
>>      if (!(attr in attributes)) {
>>        return null; // Doesn't work for numbers!
>>      }
>>      return attributes[attr];
>>    }
>> }
>>
>> void main() {
>>  auto myStr = MyStruct!int(0); // Error
>> }
>
> Whenever i am faced with this situation i do one (or more then one) of the following things.
>
> struct MyStruct(T)
> {
>     T[T] attributes;
>
>    //(1) Forward the underlying access method Eg:
>    auto opBinaryRight(string s : "in")(T attrib)
>    {
>       return attrib in attributes;
>    }
>
>    //(2) make a try method.
>    bool tryAttrib(T attrib, out T outAttrib)
>    {
>        auto p = attrib in attributes;
>        if(p) outAttrib = *p;
>        return p !is null;
>    }
>
>
>
>    //(3) Give user option to set default value.
>    T attribOrDefault(T attrib, T default)
>    {
>        auto p = attrib im attributes;
>        return p is null ? default : attrib;
>    }
>
>
>    //(4) Use Nullable!T (I prefer #5 over this one)
>    Nullable!T attribOrNull(T attrib)
>    {
>        Nullable!T result;
>        auto p = attrib ib attributes;
>        if(p) result = *p;
>        return result;
>    }
>
>    //(5) Use a pointer but not forward in operator.
>    T* attribPtr(T attrib)
>    {
>       return attrib in attributes;
>    }
>
>    //(6) Throw exception (I only do this in combination with one of the above)
>    T attribEx(T attrib)
>    {
>      return *enforce!AttribNotFoundEx(attrib in attributes);
>    }
> }

Thanks for this brief outline.

> My personal preference using #2 and #3 in combination. #2 covers the basic case "Is this thing avalible?" and #3 covers the case "Give it to me if it is avalible or use this default value" I think it gives a clear image of what your code is doing at the callsite. Only using #2 or #3 limits you in this sence.

Personally I don't like the idea of passing a default value on the user side in this particular case. If the attribute has not been set, there is a reason, and I don't want to operate with a return value of something that has not been set at all.

I introduced a check similar to #2:

bool hasAttribute(T attr) { ... }

Of course, the user has to use if. Experimentally, I introduced

 auto getAttribute(T attr) {
    if (!(attr in attributes)) {
      return T.init;
    }
    return attributes[attr];
  }

to avoid the if statement and just gently move along, if the attribute has not been set, which again leads to the problem of #3, i.e. potentially operating with a value of something that does not exist in the first place.

> For #1, #4 and #5 i personally stay away from them. They force the caller to either use an if or potentially trigger a null pointer derecerence. (Btw what is the benefit of #4? I have never used it since it seems pointless)

#4 is weird, but that's because I don't fully understand the concept behind it.

> I very rarly use attribEx. I don't think code shuld just spew exceptions all over the place. They should be reserved for really bad stuff, like bounds checks. One exception i make to this rule is if i'm dealing with ranges. Since the other methods don't lend themselfs for UFCS-chaing.

I agree. Exceptions should be reserved for serious cases or cases where you simply cannot predict all cases (reading random input from the internet, for example).
February 07, 2014
Am Mon, 03 Feb 2014 10:25:17 +0000
schrieb "Chris" <wendlec@tcd.ie>:

> MyStruct(T) {
>    T[T] attributes;
>    // ....
>    public auto getAttribute(T attr) {
>        if (!(attr in attributes)) {
>          return null; // Doesn't work for numbers!
>        }
>        return attributes[attr];
>      }
> }
> 
> void main() {
>    auto myStr = MyStruct!int(0); // Error
> }

MyStruct(T) {
   T[T] attributes;
   // ....
   public auto getAttribute(T attr) {
       return attr in attributes;
     }
}

There you go.

-- 
Marco

1 2
Next ›   Last »