Adding the ?. null verification
H. S. Teoh via Digitalmars-d
digitalmars-d at puremagic.com
Thu Jun 19 09:52:40 PDT 2014
On Thu, Jun 19, 2014 at 10:35:59AM +0200, Timon Gehr via Digitalmars-d wrote:
> On 06/18/2014 09:36 PM, H. S. Teoh via Digitalmars-d wrote:
> >Here's a first stab at a library solution:
> >
> > /**
> > * Simple-minded implementation of a Maybe monad.
> > *
>
> Nitpick: Please do not call it a 'Maybe monad'.
> It is not a monad: It's neither a functor not does it have a μ
> operator. (This could be fixed though.) Furthermore, opDispatch does
> not behave analogously to a (restricted) monadic bind operator:
>
> class C{ auto foo=maybe(C.init); }
>
> void main(){
> import std.stdio;
> C c=new C;
> writeln(maybe(c).foo); // Maybe(Maybe(null))
> }
>
> The result should be Maybe(null), if the data type was to remotely
> resemble a monad.
Here's a slightly improved version that collapses nested wrappers into a
single wrapper, so that Maybe!(Maybe!(Maybe!...Maybe!T)...) == Maybe!T:
/**
* A safe-dereferencing wrapper resembling a Maybe monad.
*
* If the wrapped object is null, any further member dereferences will simply
* return a wrapper around the .init value of the member's type. Since non-null
* member dereferences will also return a wrapped value, any null value in the
* middle of a chain of nested dereferences will simply cause the final result
* to default to the .init value of the final member's type.
*/
template SafeDeref(T)
{
static if (is(T U == SafeDeref!V, V))
{
// Merge SafeDeref!(SafeDeref!X) into just SafeDeref!X.
alias SafeDeref = U;
}
else
{
struct SafeDeref
{
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 safeDeref((t is null) ? Memb.init
: __traits(getMember, t,
field));
} else {
return safeDeref(__traits(getMember, t, field));
}
}
}
}
}
/**
* Wraps an object in a safe dereferencing wrapper resembling a Maybe monad.
*
* If the object is null, then any further member dereferences will just return
* a wrapper around the .init value of the wrapped type, instead of
* dereferencing null. This applies recursively to any element in a chain of
* dereferences.
*
* Params: t = data to wrap.
* Returns: A wrapper around the given type, with "safe" member dereference
* semantics.
*/
auto safeDeref(T)(T t)
{
return SafeDeref!T(t);
}
unittest
{
class Node
{
int val;
Node left, right;
this(int _val, Node _left=null, Node _right=null)
{
val = _val;
left = _left;
right = _right;
}
}
auto tree = new Node(1,
new Node(2),
new Node(3,
null,
new Node(4)
)
);
import std.stdio;
writeln(safeDeref(tree).right.right.val);
writeln(safeDeref(tree).left.right.left.right);
writeln(safeDeref(tree).left.right.left.right.val);
}
// Static test of monadic composition of SafeDeref.
unittest
{
{
struct Test {}
alias A = SafeDeref!Test;
alias B = SafeDeref!A;
static assert(is(B == SafeDeref!Test));
static assert(is(SafeDeref!B == SafeDeref!Test));
}
// Timon Gehr's original test case
{
class C
{
auto foo = safeDeref(C.init);
}
C c = new C;
//import std.stdio;
//writeln(safeDeref(c).foo); // SafeDeref(SafeDeref(null))
import std.string;
auto type = "%s".format(safeDeref(c).foo);
assert(type == "SafeDeref!(C)(null)");
}
}
> Furthermore, 'Maybe' is a more natural name for a type constructor
> that adds an additional element to another type, and 'Maybe monad' in
> particular is a term that already refers to this different meaning
> even more strongly in other communities.
Agreed. I've renamed it to "SafeDeref", since it's not really a monad
per se, just a safe dereferencing wrapper that has Maybe-monad-like
properties.
T
--
If it breaks, you get to keep both pieces. -- Software disclaimer notice
More information about the Digitalmars-d
mailing list