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