www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Nullable types

reply "Mehrdad" <wfunction hotmail.com> writes:
I've made my own nullable type, and I think it works a bit more 
naturally than Phobos's in some cases, since it takes advantage 
of alias this and typeof(null) and some stuff.

(It's designed to mimic C#'s nullable types.)

Would it be a good addition to Phobos? If not, ideas on what 
could be improved?


//=============================

private struct NullableTag { }
template isNullable(T) { enum isNullable = __traits(compiles, 
T.init == null); }

template Nullable(T)
{
	static if (isNullable!(T)) { alias T Nullable; }
	else
	{
		struct Nullable
		{
			// To tell if something is Nullable
			private alias .NullableTag NullableTag;
			private T _value;
			private bool _hasValue;

			public this(A)(auto ref A value)
				inout /+pure  safe nothrow+/
			{
				this._value = value;
				this._hasValue = true;
			}

			public this(A : typeof(null))(A)
				inout /+pure  safe nothrow+/ { }

			public  property ref const(bool) hasValue()
				const /+pure  safe nothrow+/
			{ return this._hasValue; }

			public  property ref inout(T) value()
				inout /+pure  safe+/
			{ return *(this._hasValue ? &this._value : null); }

			public int opCmp(RHS)(scope RHS rhs)
				const /+pure  safe nothrow+/
				if (!is(RHS.NullableTag == NullableTag))
			{
				int r;
				if (this.hasValue)
				{
					static if (__traits(compiles, this._value.opCmp(rhs)))
					{ r = this._value.opCmp(rhs._value); }
					else
					{ r = this._value < rhs ? -1 : (this._value > rhs ? 1 : 0); }
				}
				else { r = -1; }
				return r;
			}

			public int opCmp(RHS)(scope RHS rhs)
				const /+pure  safe nothrow+/
				if (is(RHS.NullableTag == NullableTag))
			{
				int r;
				if (this.hasValue && rhs.hasValue)
				{ r = 0; }
				else if (this.hasValue && !rhs.hasValue)
				{ r = 1; }
				else if (!this.hasValue && rhs.hasValue)
				{ r = -1; }
				else { r = this == rhs._value; }
				return r;
			}

			public int opCmp(RHS : typeof(null))(scope RHS)
				const /+pure  safe nothrow+/
			{ return this.hasValue ? 1 : 0; }

			public bool opEquals(RHS)(scope RHS rhs)
				const /+pure  safe nothrow+/
				if (!is(RHS.NullableTag == NullableTag))
			{ return this.hasValue && this._value == rhs; }

			public bool opEquals(RHS)(scope RHS rhs)
				const /+pure  safe nothrow+/
				if (is(RHS.NullableTag == NullableTag))
			{
				return this.hasValue == rhs.hasValue &&
					this._value == rhs._value;
			}

			public bool opEquals(RHS : typeof(null))(scope RHS)
				const /+pure  safe nothrow+/
			{ return !this.hasValue; }

			static if (!is(T == const(T)))
			{
				public auto ref opAssign(RHS)(auto ref RHS rhs)
					/+pure  safe nothrow+/
				{
					this._value = rhs;
					this._hasValue = true;
					return rhs;
				}

				public auto ref opAssign(RHS : typeof(null))(auto ref RHS rhs)
					/+pure  safe nothrow+/
				{
					this._value = T.init;
					this._hasValue = false;
					return rhs;
				}
			}

			public alias value this;
		}
	}
}

unittest
{
	Nullable!int a = null;
	Nullable!int b = 5;
	int c = 5;
	assert(a != b);
	assert(b == c);
	assert(a == null);
	assert(b != null);
	assert(b + 1 == 6);
	struct S
	{
		public bool opEquals(S) const pure  safe nothrow
		{ return true; }
		public bool opEquals(int) const pure  safe nothrow
		{ return true; }
	}
	Nullable!S s;
	assert(s != 0);
	assert(s.opCmp(null) == 0);
	assert(a.opCmp(null) == 0);
	assert(b.opCmp(null) > 0);
	assert(b.opCmp(6) < 0);
	assert(b.opCmp(5) == 0);
}

 property Nullable!(T) nullable(T)(auto ref T value) /+pure  safe 
nothrow+/
{
	Nullable!(T) result = value;
	return result;
}
Jun 20 2012
next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
Mehrdad:

 Would it be a good addition to Phobos? If not, ideas on what 
 could be improved?

What are the advantages over this one? http://dlang.org/phobos/std_typecons.html#Nullable Bye, bearophile
Jun 20 2012
prev sibling next sibling parent "Mehrdad" <wfunction hotmail.com> writes:
On Wednesday, 20 June 2012 at 16:52:26 UTC, bearophile wrote:
 Mehrdad:

 Would it be a good addition to Phobos? If not, ideas on what 
 could be improved?

What are the advantages over this one? http://dlang.org/phobos/std_typecons.html#Nullable Bye, bearophile

I thought I mentioned? 1. It uses typeof(null) to _actually_ integrate with _null_, instead of making up methods like nullify(), get(), etc. 2. It tries to do what C# does. (opEquals, opCmp, etc.)
Jun 20 2012
prev sibling next sibling parent "Mehrdad" <wfunction hotmail.com> writes:
On Wednesday, 20 June 2012 at 17:43:27 UTC, Mehrdad wrote:
 I thought I mentioned?

 1. It uses typeof(null) to _actually_ integrate with _null_, 
 instead of making up methods like nullify(), get(), etc.

 2. It tries to do what C# does. (opEquals, opCmp, etc.)

In case that wasn't clear... 1. You can't pass Phobos's Nullable type to something and expect "foo = null;" to work... even though it's supposed to be "nullable". The interface is pretty wacky. (opAssign, opCmp, opEquals) 2. It doesn't properly handle the comparison of e.g. Nullable!bool (or anything else for that matter). 3. It uses .clear(_value), which I believe is unnecessary. Just _value = T.init should be enough. etc. Also, side point: I don't understand the point of Nullable(T, T nullValue) or NullableRef(T)... are they /actually/ useful? Or were they just there to span the entire vector space, so to speak? :P
Jun 20 2012
prev sibling parent "Jesse Phillips" <jessekphillips+D gmail.com> writes:
On Wednesday, 20 June 2012 at 17:43:27 UTC, Mehrdad wrote:
 On Wednesday, 20 June 2012 at 16:52:26 UTC, bearophile wrote:
 Mehrdad:

 Would it be a good addition to Phobos? If not, ideas on what 
 could be improved?

What are the advantages over this one? http://dlang.org/phobos/std_typecons.html#Nullable Bye, bearophile

I thought I mentioned? 1. It uses typeof(null) to _actually_ integrate with _null_, instead of making up methods like nullify(), get(), etc. 2. It tries to do what C# does. (opEquals, opCmp, etc.)

It sounds to me like you address some bugs in the existing implementation, And possible poor design/limitation of the early days. You should probably add 2 as a bug report, then you could have a pull request to address them. Item 1 would be a breaking change, though in my limited view is the right direction. It would not be good for a pull request to address both 1 & 2 together. Unless of course you end up with more feedback contradicting such claim.
Jun 20 2012