www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - The problem with Nullable: nullable(null).isNull is false.

reply Piotr Mitana <piotr.mitana e.email> writes:
Hello. As Nullable has gained a bit recently (disabling automatic 
aliasing to content andrange interface – thanks for that!), it 
needs one more improvement.

Currently:

```d
assert(nullable(null).isNull == false);
```

It's might be very misleading – to make it truly safe, an object 
in nullable requires a double-check:

```d
class C {}

// ...

Nullable!C aThing = someCode()

if(!aThing.isNull && aThing.get != null) {
     // Now we are safe
}

```

The simple solution is to use the `Nullable(T, nullValue)(T t)` 
variant, as

```d
assert(nullable!null(null).isNull == true);
```

The solution in my opinion is:

- disable `Nullable(T)` for any type that can have `null` value,
- for `Nullable(T, T nullValue)` add default the `nullValue` to 
`null` for these types, so that it replaces the `Nullable(T)` 
usage.

I know that it changes code's behavior, so would require the 
deprecation process probably. However, I expect the Nullable in 
its current form is rarely used in conjunction with classes 
because of having this exact issue. Implementing this change 
would allow it to be used as a proper optional type that could 
serve to prevent null-related errors.
Jul 25 2022
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 25.07.22 10:58, Piotr Mitana wrote:
 Hello. As Nullable has gained a bit recently (disabling automatic 
 aliasing to content andrange interface – thanks for that!), it needs one 
 more improvement.
 
 Currently:
 
 ```d
 assert(nullable(null).isNull == false);
 ```
 
 It's might be very misleading – to make it truly safe, an object in 
 nullable requires a double-check:
 
 ```d
 class C {}
 
 // ...
 
 Nullable!C aThing = someCode()
 
 if(!aThing.isNull && aThing.get != null) {
      // Now we are safe
 }
 
 ```
 
 The simple solution is to use the `Nullable(T, nullValue)(T t)` variant, as
 
 ```d
 assert(nullable!null(null).isNull == true);
 ```
 
 The solution in my opinion is:
 
 - disable `Nullable(T)` for any type that can have `null` value,
 - for `Nullable(T, T nullValue)` add default the `nullValue` to `null` 
 for these types, so that it replaces the `Nullable(T)` usage.
 
 I know that it changes code's behavior, so would require the deprecation 
 process probably. However, I expect the Nullable in its current form is 
 rarely used in conjunction with classes because of having this exact 
 issue. Implementing this change would allow it to be used as a proper 
 optional type that could serve to prevent null-related errors.
Not a big fan. E.g., this ad-hoc special case would make it hard to use Nullable reliably in generic code.
Jul 25 2022
parent Piotr Mitana <piotr.mitana e.email> writes:
On Monday, 25 July 2022 at 12:04:35 UTC, Timon Gehr wrote:
 Not a big fan. E.g., this ad-hoc special case would make it 
 hard to use Nullable reliably in generic code.
Could you come up with an example of use case that would be problematic? I perceive Nullable as a way to add a null value to the type that does not have it by default (for example `Nullable!int` etc.). As classes and reference types already do have their `null`, The Nullable in a general case would serve as "add the null *if if there isin't one*. The `Nullable!int` and `Nullable!MyClass` both would have a single null value, which I believe will be even more convenient to use in a generic code. Also, a [library with an Optional type](https://code.dlang.org/packages/optional) has been written and in its README it actually states the problem that I have raised here. With Nullable covering this case there would be no need for this library, as Phobos' solution would be a proper solution for this problem.
Jul 25 2022
prev sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 7/25/22 4:58 AM, Piotr Mitana wrote:
 Hello. As Nullable has gained a bit recently (disabling automatic 
 aliasing to content andrange interface – thanks for that!), it needs one 
 more improvement.
 
 Currently:
 
 ```d
 assert(nullable(null).isNull == false);
 ```
 
 It's might be very misleading – to make it truly safe, an object in 
 nullable requires a double-check:
 
 ```d
 class C {}
 
 // ...
 
 Nullable!C aThing = someCode()
 
 if(!aThing.isNull && aThing.get != null) {
      // Now we are safe
 }
 
 ```
 
 The simple solution is to use the `Nullable(T, nullValue)(T t)` variant, as
 
 ```d
 assert(nullable!null(null).isNull == true);
 ```
 
 The solution in my opinion is:
 
 - disable `Nullable(T)` for any type that can have `null` value,
 - for `Nullable(T, T nullValue)` add default the `nullValue` to `null` 
 for these types, so that it replaces the `Nullable(T)` usage.
A counter example: ```d Nullable!C getDBValue(int key); auto x = getDBValue(42); if(x.isNull) { writeln("database does not contain the requested row"); } else if(x.get is null) { writeln("database contains the requested row, but the value is set to NULL"); } ``` The real "problem" is that Nullable is called "Nullable". -Steve
Jul 25 2022
next sibling parent Piotr Mitana <pm example.com> writes:
On Monday, 25 July 2022 at 13:05:08 UTC, Steven Schveighoffer 
wrote:
 A counter example:

 ```d
 Nullable!C getDBValue(int key);

 auto x = getDBValue(42);
 if(x.isNull) { writeln("database does not contain the requested 
 row"); }
 else if(x.get is null) { writeln("database contains the 
 requested row, but the value is set to NULL"); }
 ```

 The real "problem" is that Nullable is called "Nullable".

 -Steve
You're right, this case justifies double-checking, as there are two ways of not having a real value (either the given key is absent or the value for it is `null`). Still, this is a somewhat specific case. But that indeed means that both these options should remain available. Maybe an alias could be added then to differentiate it – something like ```d alias SmartNullable(T)(T t) = Nullable!(T, null) ``` (probably with a different name & aliasing to the plain nullable for primitives and structs).
Jul 25 2022
prev sibling parent reply jfondren <julian.fondren gmail.com> writes:
On Monday, 25 July 2022 at 13:05:08 UTC, Steven Schveighoffer 
wrote:
 The real "problem" is that Nullable is called "Nullable".
History of this poor type: 1. it's a way to add a Null state to a value type. You know, like Java. Very convenient type for trivial task. 2. hey! This doesn't act like an optional type/maybe monad! This is BROKEN. (convenience was deliberately broken to make it worse for the original task.) 3. hey! This acts like an optional type/maybe monad that doesn't care if the wrapped type also has something like a null state! This is BROKEN. (now proposed: deliberately break it to make it worse as an optional type.) What it needed was not code but a clear justification and context in the documentation, without assuming that people would just get it from the feature list.
Jul 26 2022
parent reply Paul Backus <snarwin gmail.com> writes:
On Tuesday, 26 July 2022 at 15:21:36 UTC, jfondren wrote:
 On Monday, 25 July 2022 at 13:05:08 UTC, Steven Schveighoffer 
 wrote:
 The real "problem" is that Nullable is called "Nullable".
History of this poor type: 1. it's a way to add a Null state to a value type. You know, like Java. Very convenient type for trivial task. 2. hey! This doesn't act like an optional type/maybe monad! This is BROKEN. (convenience was deliberately broken to make it worse for the original task.) 3. hey! This acts like an optional type/maybe monad that doesn't care if the wrapped type also has something like a null state! This is BROKEN. (now proposed: deliberately break it to make it worse as an optional type.)
This makes for a nice story, but doesn't actually match the facts. Since it was introduced in 2009 [1], `Nullable` has always had a distinct `isNull` state, even for types that can already be `null`. [1] https://github.com/dlang/phobos/commit/0c142994d9#diff-81bed7f05cbd4e992067b7019125e6a1349ebe5098c6980b64bbbca8d5491e17
Jul 26 2022
parent monkyyy <crazymonkyyy gmail.com> writes:
On Tuesday, 26 July 2022 at 15:34:20 UTC, Paul Backus wrote:
 This makes for a nice story, but doesn't actually match the 
 facts.
I like this story tho and need a story to believe in for why std.nullable is so so incredibly bad.
Jul 31 2022