Re: Open question: what code pattern you use usually for null safety problem

2021-01-15 Thread Basile B. via Digitalmars-d-learn

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





Re: Open question: what code pattern you use usually for null safety problem

2021-01-15 Thread Dukc via Digitalmars-d-learn

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.





Re: Open question: what code pattern you use usually for null safety problem

2021-01-15 Thread ddcovery via Digitalmars-d-learn
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!!!






Re: Open question: what code pattern you use usually for null safety problem

2021-01-15 Thread ddcovery via Digitalmars-d-learn
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;
}
}


Re: Open question: what code pattern you use usually for null safety problem

2021-01-15 Thread Imperatorn via Digitalmars-d-learn
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


Re: Open question: what code pattern you use usually for null safety problem

2021-01-15 Thread Steven Schveighoffer via Digitalmars-d-learn

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


Re: Open question: what code pattern you use usually for null safety problem

2021-01-15 Thread Steven Schveighoffer via Digitalmars-d-learn

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


Re: Open question: what code pattern you use usually for null safety problem

2021-01-15 Thread ddcovery via Digitalmars-d-learn

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.


This is only an open question to know what code patterns you 
usually use to solve this situation in D




I'm writing a "personal" article/study about "null safety" 
anti-pattern in form of github project (to include some examples)


I really thank you for your answers here that I will use (and 
mention with your permission) in this small article.


The actual version can be found here 
https://github.com/ddcovery/d_null_safety/blob/main/README.md


It is under construction :-).





Re: Open question: what code pattern you use usually for null safety problem

2021-01-14 Thread ddcovery via Digitalmars-d-learn

On Thursday, 14 January 2021 at 20:35:49 UTC, Dennis wrote:

On Thursday, 14 January 2021 at 18:24:44 UTC, ddcovery wrote:
If it's not a bother, I'd like to know how you usually 
approach it


Usually I don't deal with null because my functions get 
primitive types, slices, or structs. `ref` parameters can be 
used to replace pointers that may not be null.

When something is nullable by design, I usually do this:

```
if (!person) {
return; // early return if possible
}
if (auto f0 = person.father) {
if (auto f1 = f0.father) {
   if (f1.name == "Peter") {
   doSomething();
   }
}
}
```

It doesn't matter whether you're working with a class, pointer, 
or struct with opCast, this works. When access patterns get 
complex the nesting may get very deep.
Only if you can't avoid this I would consider using fancy 
helper functions, otherwise just use an if-statement or the && 
operator.
I agree:  using null safety is a sign of something wrong in the 
design (the need of dealing with nulls)... but if eventually you 
need it, simple **if** or **&&** should be enough.


Curiously, languages like Dart (and its flutter framework) 
performs extensive use of null safety (null is everywhere!!!) and 
it seems that every "modern" language must deal with it.


Any case, I'm learning a lot: thank you Dennis for sharing!!!


Re: Open question: what code pattern you use usually for null safety problem

2021-01-14 Thread ddcovery via Digitalmars-d-learn
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" )

And adding some extra check like ** isAssignable!(Ret, 
typeof(null) )** we can add special treatment for not nullable 
types


  if( ns(person).father.father.age._(0) == 92 ){ ... }
  assert( ns(person).father.father.father.age._ == int.init );

If for some strange reason I ever need null safety, I think this 
is the most beautiful solution or at least a great lesson on 
templates.


Thanks a lot for the lesson, Steve



Re: Open question: what code pattern you use usually for null safety problem

2021-01-14 Thread ddcovery via Digitalmars-d-learn

On Thursday, 14 January 2021 at 19:24:54 UTC, Adam D. Ruppe wrote:

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:


I'm almost never in this situation except for reading things 
like xml or json data that may be missing.


Yes, this is the usual situation (Personally, I use "DTO" 
structured objects... that are serialized/unserialized to JSON)




So I just special cased those. My json lib doesn't return null 
per se, it returns var(null) which is allowed to just return 
more harmless nulls. Thus you write 
`person.father.father.name.get!string` and it will be empty if 
anything was null in the chain.


With dom, you can optionSelector("person > father > father > 
name").innerText and again if the selector returned null, all 
its methods also just return empty strings or whatever.


Selectors are a good option to navigate on dom/json, but on 
structured objects too.  The good think with "templates" in D is 
that this "path/selector" can be compiled internally to a 
map/filter combination completly "null" free... I was 
experimenting last days with this and I think that a functional 
orientation (using a MayBe monad implemented as Range ) is the 
best way to begin.




So the library handles these special cases and then I don't 
worry about nested nulls anywhere else since I consider it bad 
style to even be in that situation in the first place.


I agree: it is a bad style. Personally I allways use MayBe monads 
in my "DTO"s (that is the effect of having worked with scala :-).


The only "cons" with Nullable!T (the "standard" D MayBe 
equivalent) is that it is not "compatible" with Range libraries 
(it is not a Range:  you use "apply" instead "map", you have not 
"filter", you can't "chain" a range and Nullable, you can't 
"join" a range of Nullables like a Range of ranges).  This is the 
reason I'm "experimenting" with my own "MayBe" InputRange that 
can be created in the form of "Some" or "None" (it's inmutable 
contrary to what happens with Nullable) and is compatible with 
all std.algorithm (and array) library.







Re: Open question: what code pattern you use usually for null safety problem

2021-01-14 Thread Dennis via Digitalmars-d-learn

On Thursday, 14 January 2021 at 18:24:44 UTC, ddcovery wrote:
If it's not a bother, I'd like to know how you usually approach 
it


Usually I don't deal with null because my functions get primitive 
types, slices, or structs. `ref` parameters can be used to 
replace pointers that may not be null.

When something is nullable by design, I usually do this:

```
if (!person) {
return; // early return if possible
}
if (auto f0 = person.father) {
if (auto f1 = f0.father) {
   if (f1.name == "Peter") {
   doSomething();
   }
}
}
```

It doesn't matter whether you're working with a class, pointer, 
or struct with opCast, this works. When access patterns get 
complex the nesting may get very deep.
Only if you can't avoid this I would consider using fancy helper 
functions, otherwise just use an if-statement or the && operator.


Re: Open question: what code pattern you use usually for null safety problem

2021-01-14 Thread Steven Schveighoffer via Digitalmars-d-learn

On 1/14/21 1:24 PM, ddcovery wrote:
I know there is other threads about null safety and the "possible" ways 
to support this in D and so on.


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

i.e.: the incremental null check solution

if(
   person !is null &&
   person.father !is null &&
   person.father.father !is null &&
   person.father.father.name == "Peter"
)
{
   doSomething();
}

or the "monad" way

[person].
   filter!"a !is null".map!"a.father".
   filter!"a !is null".map!"a.father".
   filter!"a !is null".map!"a.name".
   each!( (name) {
     if(name == "Peter") doSomething();
   });

or, may be, you have some helper function/structs/templates

if( person.d!"father".d!"father".d!"name".get == "Peter" ){
   doSomething()
}
if( person.d!"father".d!"age".get(0) > 80 ){
   doSomething()
}

or an "xml path" like template

if( person.get!"father.father.name" == "Peter" )
if( person.get!"father.father.name.length"(0) == 5 )
if( person.get!"father.father.age"(0) > 80 )


If it's not a bother, I'd like to know how you usually approach it

Thanks!!!




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


Re: Open question: what code pattern you use usually for null safety problem

2021-01-14 Thread mw via Digitalmars-d-learn

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.


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

i.e.: the incremental null check solution


I just use this most simple one:


if(
  person !is null &&
  person.father !is null &&
  person.father.father !is null &&
  person.father.father.name == "Peter"
)
{
  doSomething();
}


Reason: easy to read and reason about, esp for non-authors of 
this piece of the code.




Re: Open question: what code pattern you use usually for null safety problem

2021-01-14 Thread Adam D. Ruppe via Digitalmars-d-learn

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:


I'm almost never in this situation except for reading things like 
xml or json data that may be missing.


So I just special cased those. My json lib doesn't return null 
per se, it returns var(null) which is allowed to just return more 
harmless nulls. Thus you write 
`person.father.father.name.get!string` and it will be empty if 
anything was null in the chain.


With dom, you can optionSelector("person > father > father > 
name").innerText and again if the selector returned null, all its 
methods also just return empty strings or whatever.


So the library handles these special cases and then I don't worry 
about nested nulls anywhere else since I consider it bad style to 
even be in that situation in the first place.


Open question: what code pattern you use usually for null safety problem

2021-01-14 Thread ddcovery via Digitalmars-d-learn
I know there is other threads about null safety and the 
"possible" ways to support this in D and so on.


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

i.e.: the incremental null check solution

if(
  person !is null &&
  person.father !is null &&
  person.father.father !is null &&
  person.father.father.name == "Peter"
)
{
  doSomething();
}

or the "monad" way

[person].
  filter!"a !is null".map!"a.father".
  filter!"a !is null".map!"a.father".
  filter!"a !is null".map!"a.name".
  each!( (name) {
if(name == "Peter") doSomething();
  });

or, may be, you have some helper function/structs/templates

if( person.d!"father".d!"father".d!"name".get == "Peter" ){
  doSomething()
}
if( person.d!"father".d!"age".get(0) > 80 ){
  doSomething()
}

or an "xml path" like template

if( person.get!"father.father.name" == "Peter" )
if( person.get!"father.father.name.length"(0) == 5 )
if( person.get!"father.father.age"(0) > 80 )


If it's not a bother, I'd like to know how you usually approach it

Thanks!!!