Thread overview
The problem with Nullable: nullable(null).isNull is false.
Jul 25, 2022
Piotr Mitana
Jul 25, 2022
Timon Gehr
Jul 25, 2022
Piotr Mitana
Jul 25, 2022
Piotr Mitana
Jul 26, 2022
jfondren
Jul 26, 2022
Paul Backus
Jul 31, 2022
monkyyy
July 25, 2022

Hello. As Nullable has gained a bit recently (disabling automatic aliasing to content andrange interface – thanks for that!), it needs one more improvement.

Currently:

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

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

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

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.

July 25, 2022
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.
July 25, 2022

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

July 25, 2022

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:

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

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

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

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:

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

July 25, 2022

On Monday, 25 July 2022 at 13:05:08 UTC, Steven Schveighoffer wrote:

>

A counter example:

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

alias SmartNullable(T)(T t) = Nullable!(T, null)

(probably with a different name & aliasing to the plain nullable for primitives and structs).

July 26, 2022

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.

July 26, 2022

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

July 31, 2022

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.