View mode: basic / threaded / horizontal-split · Log in · Help
June 20, 2012
Nullable types
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
Re: Nullable types
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
Re: Nullable types
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
Re: Nullable types
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
Re: Nullable types
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.
Top | Discussion index | About this forum | D home