Null-Coalescing Operator and Extensions

Simen Kjærås simen.kjaras at gmail.com
Tue Aug 28 13:27:28 UTC 2018


On Monday, 27 August 2018 at 14:59:20 UTC, SG wrote:
> On Monday, 27 August 2018 at 07:59:17 UTC, Simen Kjærås wrote:
>> That's the null propagation operator (?.). What SG asked for 
>> is the null-coalescing operator (??). Of course, this can also 
>> be implemented in D (albeit with a slight more horrible 
>> syntax):
>
> Exactly, and I know it is an example, but it doesn't work for 
> Variant.
>
> I was trying something like below, I need to find a way to test 
> for all Nullable types out there, right now only works for 
> Nullable!int.

Sadly, Variant's operator overloading is problematic - there 
seems to be no way to write a struct such that its operator 
overloading is preferred over Variant's, and Variant's fails to 
compile. (issue 19200: 
https://issues.dlang.org/show_bug.cgi?id=19200)

Once that issue has been fixed, this should work:

// Support aliak's optional, if available:
static if (__traits(compiles, {import optional;})) import 
optional;

struct NullCoalesce {
     static auto opBinaryRight(string op : "|", T)(T lhs) {
         return NullCoalesceImpl!T(lhs);
     }
}

struct NullCoalesceImpl(T) {
     T value;
     auto opBinary(string op : "|", R)(lazy R rhs) {
         static if (is(typeof(value.peek!R))) {
             if (auto tmp = value.peek!R)
                 return *tmp;
         } else static if (is(typeof(value.isNull))) {
             if (!value.isNull)
                 return value.get;
         } else static if (is(typeof(value.unwrap))) {
             if (auto tmp = value.unwrap)
                 return *tmp;
         } else static if (is(typeof(value == null))) {
             if (value != null)
                 return value;
         } else {
             static assert(false, "Cannot perform null-coalescing 
on non-nullable type "~T.stringof~".");
         }
         return rhs;
     }
}

alias NullCoalesce _;

unittest {
     import std.variant;
     import std.typecons;

     int* a = null;
     auto b = new int;
     assert((a |_| b) == b);
     a = new int;
     assert((a |_| b) == a);

     Variant c;
     assert((c |_| 3) == 3);
     c = 4;
     assert((c |_| 3) == 4);

     Nullable!int d;
     assert((d |_| 3) == 3);
     d = 4;
     assert((d |_| 3) == 4);

     static if (is(typeof(Optional!int))) {
         Optional!int e;
         assert((e |_| 3) == 3);
         e = 4;
         assert((e |_| 3) == 4);
     }
}

Now, as has been pointed out, that only work for null-coalescing, 
not null-propagation. It seems writers of Optional, Variant, 
SumType, and so on, have decided not to support this out of the 
box, but rather wrap it separately, like Basile B.'s 
SafeAccess[1] and aliak's dispatch[2]. There's no real obstacle 
to wrapping member access directly in Optional!T such that it 
always return a Optional!(typeof(member)), though.

I've written an Optional somewhere that does safe access out of 
the box, but it seems to be on my work computer, not this one.

--
   Simen

[1]: 
https://github.com/BBasile/iz/blob/master/import/iz/sugar.d#L1658
[2]: https://code.dlang.org/packages/optional


More information about the Digitalmars-d-learn mailing list