Thread overview
The problem with Nullable: nullable(null).isNull is false.
Jul 26
jfondren
Jul 31
monkyyy
July 25

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

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

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

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

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

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

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.