January 15, 2021
On 1/15/21 9:19 AM, Steven Schveighoffer wrote:

> 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.

And now reading the other thread about this above, it looks like this type is already written:

https://code.dlang.org/packages/optional

I'd say use that.

-Steve
January 15, 2021
On Friday, 15 January 2021 at 14:25:09 UTC, Steven Schveighoffer wrote:
> On 1/15/21 9:19 AM, Steven Schveighoffer wrote:
>
>> 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.
>
> And now reading the other thread about this above, it looks like this type is already written:
>
> https://code.dlang.org/packages/optional
>
> I'd say use that.
>
> -Steve

That could be useful actually
January 15, 2021
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;
    }
}
January 15, 2021
On Friday, 15 January 2021 at 14:25:09 UTC, Steven Schveighoffer wrote:
> On 1/15/21 9:19 AM, Steven Schveighoffer wrote:
>
>> 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.
>
> And now reading the other thread about this above, it looks like this type is already written:
>
> https://code.dlang.org/packages/optional
>
> I'd say use that.
>
> -Steve

Yes, the Optional/Some/None pattern is the "functional" orientation for avoiding the use of "null".

Swift uses a similar pattern (and scala too) and supports the "null safety operators"  ?. and ??  (it doesn't work on "null" but on optional/nil).

The more I think about it, the more fervent defender of the use of ?. and ?? I am.

The misinterpretation about "null safety" is we talk about "null" reference safety, but this pattern can be used with "optional" to...

D has not optional/none/some native implementation and this is the reason we think about "?." as a "bad pattern" because we imagine it is for "null" values exclusively.

But like other operators, they could be overloaded and adapted to each library.

Well, I'm digressing:  good night!!!




January 15, 2021
On Thursday, 14 January 2021 at 18:24:44 UTC, ddcovery wrote:
> This is only an open question to know what code patterns you usually use to solve this situation in D:
>
>   if(person.father.father.name == "Peter") doSomething();
>   if(person.father.age > 80 ) doSomething();
>
> knowing that *person*, or its *father* property can be null
>

Probably the incremental check solution. A helper function if I find myself doing that more than two or three times.

On the other hand, I don't have to do this that often. I usually design the functions to either except non-null values, or to return early in case of null.


January 15, 2021
On Thursday, 14 January 2021 at 18:24:44 UTC, ddcovery wrote:
> I know there is other threads about null safety and the "possible" ways to support this in D and so on.
> [...]
> If it's not a bother, I'd like to know how you usually approach it
>
> [...]
>
> Thanks!!!

I have a opDispatch solution here [1], probably very similar to the other opDispatch solution mentioned. It is used in d-scanner since several years, e.g here [2]. I'd like to have this as a first class operator because as usual in D,  you can do great things with templates but then completion is totally unable to deal with them. Also There's a great difference between using the template to do refacts and using it to write new code. Very frustrating to write `safeAcess(stuff). ` and no completion popup appears.

[1]: https://gitlab.com/basile.b/iz/-/blob/master/import/iz/sugar.d#L1655
[2]: https://github.com/dlang-community/D-Scanner/blob/2963358eb4a24064b0893493684d4075361297eb/src/dscanner/analysis/assert_without_msg.d#L42


1 2
Next ›   Last »