Thread overview
Nullable types
Jun 20, 2012
Mehrdad
Jun 20, 2012
bearophile
Jun 20, 2012
Mehrdad
Jun 20, 2012
Mehrdad
Jun 21, 2012
Jesse Phillips
Apr 20, 2016
zashi
June 20, 2012
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;
}
June 20, 2012
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
June 20, 2012
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.)
June 20, 2012
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
June 21, 2012
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.
April 20, 2016
On Wednesday, 20 June 2012 at 16:31:34 UTC, Mehrdad wrote:
> 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.
>
> [...]

Mehrdad, you sir, are a wizard. Hat off to you!