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.

Reply via email to