On Friday, 15 January 2021 at 14:19:35 UTC, Steven Schveighoffer wrote:
On 1/14/21 7:27 PM, ddcovery wrote:
On Thursday, 14 January 2021 at 20:23:08 UTC, Steven Schveighoffer wrote:

You could kinda automate it like:

struct NullCheck(T)
{
   private T* _val;
   auto opDispatch(string mem)() if (__traits(hasMember, T, mem)) {        alias Ret = typeof(() { return __traits(getMember, *_val, mem); }());
       if(_val is null) return NullCheck!(Ret)(null);
       else return NullCheck!(Ret)(__trats(getMember, *_val, mem));
   }

   bool opCast(V: bool)() { return _val !is null; }
}

auto nullCheck(T)(T *val) { return AutoNullCheck!T(val);}

// usage
if(nullCheck(person).father.father && person.father.father.name == "Peter")

Probably doesn't work for many circumstances, and I'm sure I messed something up.

-Steve

I'm seeing "opDispatch" everywhere last days :-). It's really powerful!!!

If we define an special T _(){ return _val; } method, then you can write

   if( nullCheck(person).father.father.name._ == "Peter")

And renaming

   if( ns(person).father.father.name._ == "Peter" )

This doesn't work, if person, person.father, or person.father.father is null, because now you are dereferencing null again.

But something like this might work:

NullCheck(T)
{
   ... // opdispatch and stuff
   bool opEquals(auto ref T other) {
      return _val is null ? false : *_val == other;
   }
}

Something similar to BlackHole or WhiteHole. Essentially there's a default action for null for all types/fields/methods, and everything else is passed through.

Swift has stuff like this built-in. But D might look better because you wouldn't need a chain of question marks.

-Steve

I don't know if I can add this to Dlang IDE and then share a link... links that I generate doesn't work...

* I have adapted the "onDispatch" and the factory method to manage nullable and not nullable values * The unwrapper "T _()" method returns Nullable!T for nullable value types instead T (similar to c#)

* I removed the T* when testing changes (I discovered after 1000 changes that template errors are not well informed by the compiler... I losted a lot to discover a missing import)... I will try to restore.


import std.typecons;
import std.traits;

void main()
{
Person person = new Person("Andres", 10, new Person("Peter", 40, null));
    // null reference
    assert(ns(person).father.father._ is null);
    // null reference
    assert(ns(person).father.father.name._ is null);
    // reference value
    assert(ns(person).father.name._ == "Peter");
    // Nullable!int
    assert(ns(person).father.father.age._.isNull);
    assert(ns(person).father.father.age._.get(0) == 0);
    assert(ns(11)._.get == 11);
}

struct NullSafety(T)
{
    private T _val;
    private bool _isEmpty;

auto opDispatch(string name)() if (__traits(hasMember, T, name))
    {
alias Ret = typeof((() => __traits(getMember, _val, name))());
        if (_val is null)
        {
            static if (isAssignable!(Ret, typeof(null)))
                return NullSafety!(Ret)(null, true);
            else
                return NullSafety!(Ret)(Ret.init, true);
        }
        else
        {
return NullSafety!(Ret)(__traits(getMember, _val, name), false);
        }
    }

static if (isAssignable!(T, typeof(null))) // Reference types unwrapper
        T _()
        {
            return _val;
        }
    else // value types unwrapper
        Nullable!T _()
        {
            return _isEmpty ? Nullable!T() : Nullable!T(_val);
        }

}

auto ns(T)(T val)
{
    static if (isAssignable!(T, typeof(null)))
        return NullSafety!T(val, val is null);
    else
        return NullSafety!T(val, false);
}

class Person
{
    public string name;
    public Person father;
    public int age;
    this(string name, int age, Person father)
    {
        this.name = name;
        this.father = father;
        this.age = age;
    }
}

Reply via email to