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

Reply via email to