Jump to page: 1 2
Thread overview
Nullable!T with T of class type
Jun 25, 2018
kdevel
Jun 25, 2018
Jonathan M Davis
Jun 26, 2018
Nathan S.
Jun 26, 2018
kdevel
Jun 26, 2018
Jonathan M Davis
Jun 28, 2018
kdevel
Jun 28, 2018
Jonathan M Davis
Jun 28, 2018
kdevel
Jun 28, 2018
Jonathan M Davis
Jun 28, 2018
Jordan Wilson
Jun 28, 2018
aliak
Jun 26, 2018
Nathan S.
Jun 26, 2018
kdevel
June 25, 2018
Just stumbled over the following design:

   class S {...}

   class R {
      :
      Nullable!S s;
      :
   }

s was checked in code like

   R r;
   :
   if (r.s is null)
      throw new Exception ("some error message");

At runtime the following was caught:

    fatal error: caught Throwable: Called `get' on null Nullable!S

Why can't this programming error be detected at compile time? Is it possible
to "lower" the Nullable operations if T is a class type such that there
is only one level of nullification?

BTW: https://dlang.org/library/std/typecons/nullable.html contains duplicate sections:

    Function nullable
    Function nullable
    Struct Nullable
    Struct Nullable

June 25, 2018
On Monday, June 25, 2018 19:40:30 kdevel via Digitalmars-d-learn wrote:
> Just stumbled over the following design:
>
>     class S {...}
>
>     class R {
>
>        Nullable!S s;
>
>     }
>
> s was checked in code like
>
>     R r;
>
>     if (r.s is null)
>        throw new Exception ("some error message");
>
> At runtime the following was caught:
>
>      fatal error: caught Throwable: Called `get' on null Nullable!S
>
> Why can't this programming error be detected at compile time?

If you have a function that accepts Nullable!T, when that function is called, how on earth is it going to know whether the argument it received was null at compile time? That depends entirely on the argument, which could have come from anywhere. So, in the general case, the compiler can't possibly determine whether a variable is null or not at compile time.

And as far as just within a function goes, Nullable is a library type. There's nothing special about it. The compiler has no magic knowledge about what it does or how it functions. So, how would it know that

R r;
r.foo();

was bad code? And honestly, this sort of problem actually gets surprisingly thorny even if you tried to bake this into the compiler for a built-in type. Sure, it would be straightforward for the compiler to see that

int* i;
*i = 42;

is dereferencing null, but as soon as you start adding if statements, function calls, etc. it quickly becomes impossible for the compiler to accurately determine whether the pointer is null or not. Any such attempt will inevitably end up with false positives, forcing you to do stuff like assign variables values when you know that it's unnecessary - it would complicate the compiler considerably to even make the attempt. It's far simpler to just treat it as a runtime error. Even more restricted languages such as Java do that. Java does try to force you to initialize stuff (resulting in annoying false positives at times), but in general, it still can't guarantee when a variable is null or not and is forced to insert runtime null checks.

> Is it possible
> to "lower" the Nullable operations if T is a class type such that
> there
> is only one level of nullification?

It's been discussed before, but doing so would make the behavior of Nullable depend subtly on what type it contained and risks bugs in generic code. Also, there _is_ code out there which depends on the fact that you can have a Nullable!MyClass where the variable has a value and that value is a null reference. If you really don't want the extra bool, then just don't use Nullable. Class references are already naturally nullable.

- Jonathan M Davis

June 26, 2018
On Monday, 25 June 2018 at 19:40:30 UTC, kdevel wrote:
> Is it possible
> to "lower" the Nullable operations if T is a class type such that there
> is only one level of nullification?

Yes: https://run.dlang.io/is/hPxbyf

----
template Nullable(S)
{
    import std.traits : isPointer, isDynamicArray;
    static if (is(S == class) || is(S == interface)
               || is(S == function) || is(S == delegate)
               || isPointer!S || isDynamicArray!S)
    {
        alias Nullable = S;
    }
    else
    {
        static import std.typecons;
        alias Nullable = std.typecons.Nullable!S;
    }
}
----

June 26, 2018
On Monday, 25 June 2018 at 22:58:41 UTC, Jonathan M Davis wrote:
> Java does try to force you to initialize stuff (resulting in annoying false positives at times), but in general, it still can't guarantee when a variable is null or not and is forced to insert runtime null checks.

Java can be somewhat clever about this though. Often it just needs to perform a single null check in a method body and thereafter knows the pointer can't be null and elides the check.
June 26, 2018
On Monday, 25 June 2018 at 22:58:41 UTC, Jonathan M Davis wrote:
> On Monday, June 25, 2018 19:40:30 kdevel via Digitalmars-d-learn wrote:
>>     R r;
>>
>>     if (r.s is null)
>>        throw new Exception ("some error message");

[...]

>> Why can't this programming error be detected at compile time?
>
> If you have a function that accepts Nullable!T, when that function is called, how on earth is it going to know whether the argument it received was null at compile time?

That was not the error. The error was that I used is null instead of .isNull() which lead to a different Exception (Error).

[...]

> If you really don't want the extra bool, then just don't use Nullable. Class references are already naturally nullable.

Sure.
June 26, 2018
On Tuesday, 26 June 2018 at 14:32:59 UTC, Nathan S. wrote:
> On Monday, 25 June 2018 at 19:40:30 UTC, kdevel wrote:
>> Is it possible
>> to "lower" the Nullable operations if T is a class type such that there
>> is only one level of nullification?
>
> Yes: https://run.dlang.io/is/hPxbyf
>
> ----
> template Nullable(S)

[...]

> ----

Works nicely. Thanks!
June 26, 2018
On Tuesday, June 26, 2018 19:03:20 kdevel via Digitalmars-d-learn wrote:
> On Monday, 25 June 2018 at 22:58:41 UTC, Jonathan M Davis wrote:
> > On Monday, June 25, 2018 19:40:30 kdevel via
> >
> > Digitalmars-d-learn wrote:
> >>     R r;
> >>
> >>     if (r.s is null)
> >>
> >>        throw new Exception ("some error message");
>
> [...]
>
> >> Why can't this programming error be detected at compile time?
> >
> > If you have a function that accepts Nullable!T, when that function is called, how on earth is it going to know whether the argument it received was null at compile time?
>
> That was not the error. The error was that I used is null instead
> of .isNull() which lead to a different Exception (Error).

Well, get checks whether the Nullable isNull, and throws an Error if it is, which is what you showed in your original post. If you want a Nullable to have the value of null, then you have to explicitly set it to null. Nullable really doesn't have anything to do with the null value for pointers or references beyond the fact that it allows a way to emulate that behavior for non-nullable types by using a bool.

isNull returns whether the Nullable has been given a value or not, whereas checking the value with is null means checking whether it has a value and whether that value is null. If that behavior is not what you want, then you will have to find a different solution, though honestly, I don't understand why folks keep trying to put nullable types in Nullable in non-generic code.

- Jonathan M Davis

June 28, 2018
On Tuesday, 26 June 2018 at 21:54:49 UTC, Jonathan M Davis wrote:
> [H]onestly, I don't understand why folks keep trying to put nullable types in Nullable in non-generic code.

How do you signify that a struct member of class type is optional?
June 28, 2018
On Thursday, June 28, 2018 18:10:07 kdevel via Digitalmars-d-learn wrote:
> On Tuesday, 26 June 2018 at 21:54:49 UTC, Jonathan M Davis wrote:
> > [H]onestly, I don't understand why folks keep trying to put nullable types in Nullable in non-generic code.
>
> How do you signify that a struct member of class type is optional?

Structs aren't nullable, so wrapping them in a Nullable makes perfect sense. Whether they happen to be on the stack or members of another type is irrelevant to that. It's wrapping types like pointers and class references in a Nullable that's an odd thing to do - the types where someone might ask why the extra bool is necessary in the Nullable. Wrapping them in a Nullable makes sense in generic code, because the code isn't written specifically for them, but something like Nullable!MyClass in non-generic code is pointless IMHO, because a class reference is already nullable.

- Jonathan M Davis

June 28, 2018
On Thursday, 28 June 2018 at 19:22:38 UTC, Jonathan M Davis wrote:
> Nullable makes sense in generic code, because the code isn't written specifically for them, but something like Nullable!MyClass in non-generic code is pointless IMHO, because a class reference is already nullable.

It is already technically nullable. But how do you signify to the reader of the code that a class member in a struct or in a class may intentionally (not purely technically) be null?

If I had written

    class R {
       :
       S s;
       :
    }

with S being a class. The reader of the code cannot spot if s is optional. In my code I could not have declared S as a struct since it implements an interface.
« First   ‹ Prev
1 2