Adding the ?. null verification

Meta via Digitalmars-d digitalmars-d at puremagic.com
Wed Jun 18 12:51:41 PDT 2014


On Wednesday, 18 June 2014 at 19:37:42 UTC, H. S. Teoh via 
Digitalmars-d wrote:
> Here's a first stab at a library solution:
>
> 	/**
> 	 * Simple-minded implementation of a Maybe monad.
> 	 *
> 	 * Params: t = data to wrap.
> 	 * Returns: A wrapper around the given type, with "safe" 
> member dereference
> 	 * semantics, that is, if t is null, then any further member 
> dereferences will
> 	 * just return a wrapper around the .init value of the wrapped 
> type, instead of
> 	 * deferencing the null and crashing.
> 	 */
> 	auto maybe(T)(T t) {
> 		static struct Maybe {
> 			T t;
> 	
> 			// Make the wrapper as transparent as possible.
> 			alias t this;
> 	
> 			// This is the magic that makes it all work.
> 			auto opDispatch(string field)()
> 				if (is(typeof(__traits(getMember, t, field))))
> 			{
> 				alias Memb = typeof(__traits(getMember, t, field));
> 	
> 				// If T is comparable with null, then we do a null
> 				// check. Otherwise, we just dereference the member
> 				// since it's guaranteed to be safe of null
> 				// dereferences.
> 				//
> 				// N.B.: we always return a wrapped type in case the
> 				// return type contains further nullable fields.
> 				static if (is(typeof(t is null))) {
> 					return maybe((t is null) ? Memb.init
> 								: __traits(getMember, t, field));
> 				} else {
> 					return maybe(__traits(getMember, t, field));
> 				}
> 			}
> 		}
> 		return Maybe(t);
> 	}
> 	
> 	/**
> 	 * A sample tree structure to test the above code.
> 	 */
> 	class Node {
> 		int val;
> 		Node left, right;
> 	
> 		this(int _val, Node _left=null, Node _right=null) {
> 			val = _val;
> 			left = _left;
> 			right = _right;
> 		}
> 	}
> 	
> 	void main() {
> 		import std.stdio;
> 	
> 		auto tree = new Node(1,
> 			new Node(2),
> 			new Node(3,
> 				null,
> 				new Node(4)
> 			)
> 		);
> 	
> 		writeln(maybe(tree).right.right.val);
> 		writeln(maybe(tree).left.right.left.right);
> 		writeln(maybe(tree).left.right.left.right.val);
> 	}
>
> Program output:
>
> 	4
> 	Maybe(null)
> 	0
>
> It's not perfect, but as you can see, attempting to dereference 
> null
> just returns the result type's .init value. You can also modify 
> the code
> where it returns Memb.init, to also log a message to a debug 
> log that
> indicates a possible logic problem with the code (failure to 
> check for
> null, etc.).
>
> This also allows you to do a complete null check in a single 
> statement:
>
> 	if (maybe(tree).left.right.right.left.right !is null) {
> 		// do something with that value
> 	}
>
> If nothing else, this at least saves you the trouble of having 
> to check
> every intermediate reference in the chain. :)
>
> The only wart I can see currently is the "Maybe(null)" 
> appearing in the
> writeln output, instead of just "null", but that can be worked 
> around by
> implementing a toString method in the Maybe struct that 
> forwards to the
> wrapped type's toString method (or something along those lines).
>
> Anyway, this is just a first shot at it. You can probably build
> something more sophisticated out of it. :)
>
>
> T

That's nifty. opDispatch can do some cool stuff.


More information about the Digitalmars-d mailing list