digitalmars.D.learn - Templates: generic "return null;"
- Chris (15/15) Feb 03 2014 Is there a way I can make the return type in getAttribute
- Stanislav Blinov (8/17) Feb 03 2014 One way would be to use std.typecons.Nullable(T) as a return type.
- Chris (11/29) Feb 03 2014 Thanks. I'll try this one.
- Stanislav Blinov (12/18) Feb 03 2014 D has no concept of "nothing" in the language. What if your
- Dicebot (6/6) Feb 03 2014 You have forgot to mention what behavior you are actually trying
- Chris (27/33) Feb 03 2014 Thanks. T.init actually does the trick. The behavior:
- bearophile (4/7) Feb 03 2014 It's probably better to start using Nullable.
- TheFlyingFiddle (58/73) Feb 03 2014 Whenever i am faced with this situation i do one (or more then
- Chris (25/101) Feb 04 2014 Thanks for this brief outline.
- Marco Leise (12/26) Feb 06 2014 MyStruct(T) {
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 }
Feb 03 2014
On Monday, 3 February 2014 at 10:25:19 UTC, Chris wrote:T[T] attributes; // .... public auto getAttribute(T attr) { if (!(attr in attributes)) { return null; // Doesn't work for numbers! } return attributes[attr]; } }One way would be to use std.typecons.Nullable(T) as a return type. Another would be to retink your design :) Note that for numbers, there's no distinct "does not exist" value (well, ok, we have NaN for floating point). Neither there is for structs. Generally such methods as your getAttribute either throw, or get a second optional parameter as hint on what to return when the element is not found in the dictionoary.
Feb 03 2014
On Monday, 3 February 2014 at 10:32:58 UTC, Stanislav Blinov wrote:On Monday, 3 February 2014 at 10:25:19 UTC, Chris wrote:Thanks. I'll try this one.T[T] attributes; // .... public auto getAttribute(T attr) { if (!(attr in attributes)) { return null; // Doesn't work for numbers! } return attributes[attr]; } }One way would be to use std.typecons.Nullable(T) as a return type.Another would be to retink your design :) Note that for numbers, there's no distinct "does not exist" value (well, ok, we have NaN for floating point). Neither there is for structs. Generally such methods as your getAttribute either throw, or get a second optional parameter as hint on what to return when the element is not found in the dictionoary.I'm reluctant to (over)use throw, because I think that throw should be the last resort when you cannot easily predict all the things that can go wrong. Simple requests should give simple answers. If the key doesn't exist it returns nothing. The problem above only exists because of generic types, else it would be either 'null' or for numbers -1 or something. But if you have a good point I've overlooked, you can convince me of throw. No ideology :)
Feb 03 2014
On Monday, 3 February 2014 at 10:55:23 UTC, Chris wrote:I'm reluctant to (over)use throw, because I think that throw should be the last resort when you cannot easily predict all the things that can go wrong. Simple requests should give simple answers. If the key doesn't exist it returns nothing.D has no concept of "nothing" in the language. What if your dictionary contains objects, and for some key K it *does* contain a null reference? How would you distinguish absence of value from some "invalid" value? Nullable can help with that, of course. But then again, what if your dictionary does contain other Nullables? :) Generally, inability to return value is an exceptional situation.The problem above only exists because of generic types, else it would be either 'null' or for numbers -1 or something.-1 is a number, just like 0 or 10 or 13142313. Same as above, at the call site you won't be able to tell if -1 indicates that there's no such key in the attributes dictionary or just that attribute happens to have a value of -1.
Feb 03 2014
You have forgot to mention what behavior you are actually trying to achieve ;) Common not-so-meaningful value is simply T.init , but there can be no such thing as generic sentinel. If you need cheap and simple way to figure out that attribute was missing, change API to return value by out parameter and turn normal return value into boolean success flag.
Feb 03 2014
On Monday, 3 February 2014 at 12:25:16 UTC, Dicebot wrote:You have forgot to mention what behavior you are actually trying to achieve ;) Common not-so-meaningful value is simply T.init , but there can be no such thing as generic sentinel. If you need cheap and simple way to figure out that attribute was missing, change API to return value by out parameter and turn normal return value into boolean success flag.Thanks. T.init actually does the trick. The behavior: auto name = myStruct.getAttribute("name"); if (name == "bla") { // do something } else { // do something else } or (theoretically): auto second = myStruct.getAttribute(1.0); if (second > 1.5) { // do something } else { // do something else. } I haven't got a use case for the second example, but it might be handy for data analysis and I wanted to test how far you can go with templates. The reasoning behind it is that string name; try { name = myStruct.getAttribute("name"); } // ... is a bit awkward an OTT. I'd prefer to introduce hasAttribute("name") instead, if I want to be sure it exists. if (myStruct.hasAttribute("name)) name = myStruct.getAttribute("name");
Feb 03 2014
Dicebot:If you need cheap and simple way to figure out that attribute was missing, change API to return value by out parameter and turn normal return value into boolean success flag.It's probably better to start using Nullable. Bye, bearophile
Feb 03 2014
On Monday, 3 February 2014 at 14:10:35 UTC, bearophile wrote:Dicebot: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.If you need cheap and simple way to figure out that attribute was missing, change API to return value by out parameter and turn normal return value into boolean success flag.It's probably better to start using Nullable. Bye, bearophile
Feb 03 2014
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.
Feb 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:Aha, I see. So in my scenario it is useless.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.
Feb 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 */ } );
Feb 03 2014
On Monday, 3 February 2014 at 14:10:35 UTC, bearophile wrote:Dicebot:Depends. If you control system as a whole, yes, it is better to build it on top of Nullable. If interfacing with external code is intended, it will just add more boilerplate. Also I hate name "Nullable" and use it under name "Optional" :)If you need cheap and simple way to figure out that attribute was missing, change API to return value by out parameter and turn normal return value into boolean success flag.It's probably better to start using Nullable. Bye, bearophile
Feb 03 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; } 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); } } "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 the caller to either use an if or potentially trigger a null 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.
Feb 03 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:Thanks for this brief outline.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; } 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); } }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 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. 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 does not exist in the first place.the caller to either use an if or potentially trigger a null never used it since it seems pointless)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).
Feb 04 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
Feb 06 2014