digitalmars.D - Nullable!T
- bearophile (50/50) Jul 06 2010 In C#2+ there's the Nullable type, one of its main purposes is to repres...
- Adam Ruppe (86/97) Jul 06 2010 I figure a Nullable struct would just alias the original type if T t;
- bearophile (5/9) Jul 06 2010 I don't think that's a good idea, you lose the standard API of Nullable,...
- Adam Ruppe (6/10) Jul 06 2010 Make that standard API free functions, like std.array does. Then, it
- Nick Sabalausky (5/12) Jul 06 2010 But ducks are bad. How about an INullable that's implemented by Nullable...
- Adam Ruppe (18/23) Jul 06 2010 Oh, I don't know. You don't want to over do it, but to me a nullable
- Jacob Carlborg (5/15) Jul 07 2010 Uniform function call syntax isn't implemented (issue 3382), if you're
- Adam Ruppe (3/5) Jul 07 2010 Yeah, that would be the ideal way to do it, but just having the free
- Clemens (2/13) Jul 07 2010 Strange... I could have sworn that when I recently tried D2, arrays supp...
- Adam Ruppe (4/7) Jul 07 2010 Arrays work for it, and have for quite a while, but nothing else does
- Clemens (4/12) Jul 07 2010 Now that you mention it, yes, that sounds familiar.
- Adam Ruppe (6/12) Jul 07 2010 Yes, that would work. It is necessary that it does for a nullable api
- Adam Ruppe (10/12) Jul 06 2010 Then went off and implemented something where that doesn't actually
null values coming from databases. Another purpose for D is for example find(iterable) can return a Nullable!T instead of throwing an exception (or returning -1) if no item can be found. So if seen as useful D can add a Nullable to Phobos in less than two years from now, when dmd2 has less bugs. A Nullable struct is not so complex, it can be added to Phobos2. Before creating it some design decisions are necessary, here are three of them: this hurts generic code a little. An alternative is in this case to not add the boolean field to the Nullable and make hasValue just look if the reference !is null (so if T is a class then (Nullable!T).sizeof == T.sizeof). If object references are allowed then writing the value field sets the hasValue boolean to true if T is not a class reference. Nullable!(Nullable!T) === Nullable!T to allow to write simpler generic code. hasValue property is false. D can do the same, or it can use get(default=T.init) that returns the value if present or the given default (or the default default). This can be a problem if a delegate is taken out of get because in D default values are added at call point, they are not an information stored inside the callee as in Python. So Nullable can have the methods/properties: value property hasValue get(defaukt=T.init) type alias of T Reading the value if hasValue is false generates a specific exception. An error for safety: Nullable!int x; int y = x; // compile ERROR int y = x.get(); // OK int y = x.get(5); // OK int y = x.hasValue ? x.value : x.type.init; // OK int y = x.hasValue ? x.value : -1; // OK Optionally this can be accepted: Nullable!int x; if (x) { ... that is equivalent to: if (x.hasValue) { ... But this doesn't look fully tidy to me... --------------- So far I think Nullable can be implemented with not changes in the language/compiler. Now some small things that can require such changes. Nullable!int x; int y = x.hasValue ? x.value : -1; // OK With: Nullable!int x; int y = x ?? -1; Nullable!int x; that can be written: int? x; This can be done in D too, but this can be added later too. The advantage of this syntax sugar is to encourage programmers to use Nullables. There is another possible bit of compiler help, to avoid most of those empty Nullable exceptions and make Nullable a bit safer the compiler can refuse code like: Nullable!int result = foo(); int x = result.value; And accept code like: Nullable!int result = foo(); if (result.hasValue) { int x = result.value; ... } else { ... } Bye, bearophile
Jul 06 2010
On 7/6/10, bearophile <bearophileHUGS lycos.com> wrote:I figure a Nullable struct would just alias the original type if T t; t = null; already compiles. So a nullable pointer, class, or Nullable is just a no-op. This would cover your cases 1 and 2 with a simple rule.Optionally this can be accepted: Nullable!int x; if (x) { ... that is equivalent to: if (x.hasValue) { ... But this doesn't look fully tidy to me...Blargh, what I'd want is if(x is null) An implementation that might work is ============= template Nullable(T) { static if(is(typeof( { T t; t = null; }))) { alias T Nullable; // if it is already nullable, do nothing special } else { struct Nullable { T value; T* hasValue = null; // i guess this could be boolean too bool opCast(A)() if(is(A == bool)) { // for if(x) return !(hasValue is null); } typeof(this) opAssign(T v) { // should be ok assigning known not-null values value = v; hasValue = &value; return this; } typeof(this) opAssign(typeof(this) t) { // Nullable!T = Nullable!T should always be good value = t.value; if(t.hasValue) hasValue = &value; else hasValue = null; return this; } typeof(this) opAssign(void* nullptr) { // so literal null assignment works assert(nullptr is null); hasValue = null; return this; } } } } // below is a kind of unittest class A {} import std.stdio; void main() { A a1; Nullable!A a2; a1 = a2; a2 = a1; // compatible; types are aliases Nullable!(Nullable!A) a3; a1 = a3; // still compatible, aliases int* b1; Nullable!(int*) b2; b1 = b2; b2 = b1; // aliases again int c1; Nullable!(int) c2; assert(!c2.hasValue); // it should start null //c1 = c2; // doesn't compile; they are different types c2 = c1; // does compile, and set to non-null implicitly assert(c2.hasValue); // just assigned to it; shouldn't be bull c2 = null; // should compile, set to null assert(!c2.hasValue); Nullable!(Nullable!int) c3; c3 = 10; // should be fine c2 = c3; // should compile // c1 = c3; // should not and does not if(c2) // same as if(c2.hasValue) assert(0); else writefln("value is %d", c2.value); } ==============int y = x ?? -1;Meh, the regular ternary works fine, or this could be a member function: x.get(-1); // gets if not null, returns -1 if it is null x itself is a struct, so it can never be null, so no need to worry about x.get throwing a segfault.This can be done in D too, but this can be added later too. The advantage of this syntax sugar is to encourage programmers to use Nullables.Gah, Nullable!T is perfectly fine. I don't even know if you actually want to encourage people to use nullables; they shouldn't be needed all that often says my gut. Better to avoid things being null if possible.
Jul 06 2010
Adam Ruppe:I figure a Nullable struct would just alias the original type if T t; t = null; already compiles. So a nullable pointer, class, or Nullable is just a no-op. This would cover your cases 1 and 2 with a simple rule.I don't think that's a good idea, you lose the standard API of Nullable, so it's worse than useless. or accept them too (and don't add a boolean after the T, using the null reference to store the missing T). Bye, bearophile
Jul 06 2010
On 7/6/10, bearophile <bearophileHUGS lycos.com> wrote:I don't think that's a good idea, you lose the standard API of Nullable, so it's worse than useless.Make that standard API free functions, like std.array does. Then, it would work with all nullable items, and not just Nullable ones. Uniformity is good.(and don't add a boolean after the T, using the null reference to store the missing T).This would be the next best thing. struct Nullable(T) { T value; alias value this; mixin standardApi!(this); } could do it.
Jul 06 2010
"Adam Ruppe" <destructionator gmail.com> wrote in message news:mailman.288.1278428494.24349.digitalmars-d puremagic.com...On 7/6/10, bearophile <bearophileHUGS lycos.com> wrote:But ducks are bad. How about an INullable that's implemented by Nullable and uses nice safe explicit interfaces instead of compile-time duck-typing.)I don't think that's a good idea, you lose the standard API of Nullable, so it's worse than useless.Make that standard API free functions, like std.array does. Then, it would work with all nullable items, and not just Nullable ones. Uniformity is good.
Jul 06 2010
On 7/6/10, Nick Sabalausky <a a.a> wrote:But ducks are bad.Oh, I don't know. You don't want to over do it, but to me a nullable thing is just anything where item = null; compiles; a good fit for a duck.How about an INullable that's implemented by Nullable and implicitly implemented by classes?A problem here is if the class is null, you won't be able to use it. Consider: interface INullable { bool isNull(); } class Whatever : INullable { bool isNull() { return this is null; } } void main() { Whatever a; if(a.isNull()) // hardware exception, since this is null, it can't get at the interface }uses nice safe explicit interfaces instead of compile-time duck-typing.)Yea, there is a nice benefit there, but in this specific case, I don't think it can be done due to the nature of null.
Jul 06 2010
On 2010-07-06 17.01, Adam Ruppe wrote:On 7/6/10, bearophile<bearophileHUGS lycos.com> wrote:Uniform function call syntax isn't implemented (issue 3382), if you're referring to that.I don't think that's a good idea, you lose the standard API of Nullable, so it's worse than useless.Make that standard API free functions, like std.array does. Then, it would work with all nullable items, and not just Nullable ones. Uniformity is good.-- Jacob Carlborg(and don't add a boolean after the T, using the null reference to store the missing T).This would be the next best thing. struct Nullable(T) { T value; alias value this; mixin standardApi!(this); } could do it.
Jul 07 2010
On 7/7/10, Jacob Carlborg <doob me.com> wrote:Uniform function call syntax isn't implemented (issue 3382), if you're referring to that.Yeah, that would be the ideal way to do it, but just having the free functions there are good enough to accomplish the goal.
Jul 07 2010
Jacob Carlborg Wrote:On 2010-07-06 17.01, Adam Ruppe wrote:Strange... I could have sworn that when I recently tried D2, arrays supported range methods (popFront etc), and that those were implemented in std.array. Am I mistaken, or how does that work?On 7/6/10, bearophile<bearophileHUGS lycos.com> wrote:Uniform function call syntax isn't implemented (issue 3382), if you're referring to that.I don't think that's a good idea, you lose the standard API of Nullable, so it's worse than useless.Make that standard API free functions, like std.array does. Then, it would work with all nullable items, and not just Nullable ones. Uniformity is good.
Jul 07 2010
On 7/7/10, Clemens <eriatarka84 gmail.com> wrote:Strange... I could have sworn that when I recently tried D2, arrays supported range methods (popFront etc), and that those were implemented in std.array. Am I mistaken, or how does that work?Arrays work for it, and have for quite a while, but nothing else does yet; it doesn't work with classes and pointers, which are the other important built-in nullables.
Jul 07 2010
Adam Ruppe Wrote:On 7/7/10, Clemens <eriatarka84 gmail.com> wrote:Now that you mention it, yes, that sounds familiar. That brings up an interesting corner case though: once uniform call syntax is supported for pointers and references, and you have a null reference x, an actual method x.foo() would give you a segmentation fault, while a pseudomethod that is implemented as a free function could have the same call syntax, x.foo(), but could potentially work even for null references. Correct? Not saying it's necessarily a problem, just slightly unexpected.Strange... I could have sworn that when I recently tried D2, arrays supported range methods (popFront etc), and that those were implemented in std.array. Am I mistaken, or how does that work?Arrays work for it, and have for quite a while, but nothing else does yet; it doesn't work with classes and pointers, which are the other important built-in nullables.
Jul 07 2010
On 7/7/10, Clemens <eriatarka84 gmail.com> wrote:That brings up an interesting corner case though: once uniform call syntax is supported for pointers and references, and you have a null reference x, an actual method x.foo() would give you a segmentation fault, while a pseudomethod that is implemented as a free function could have the same call syntax, x.foo(), but could potentially work even for null references. Correct?Yes, that would work. It is necessary that it does for a nullable api too, since hasValue should be a call, which is the same as !is null; it needs to be able to return false if it is null, not segfault. (Ideally, I'd just like to get the "x is null" to work somehow, but however it looks, it does need to work.)
Jul 07 2010
I said:Blargh, what I'd want is if(x is null)Then went off and implemented something where that doesn't actually compile, and forgot to mention it. This is the reason I used a T* in the nullable struct; I was hoping to take T* is null and make it work for the struct as a whole, but it didn't work. (tried alias this). But I dropped it and got everything else to work, so it isn't all bad, I just forgot to mention this. It would still be nice if x is null could be gotten to work here. It wouldn't feel complete without it.
Jul 06 2010