Current sentiment on Nullable.get

Jonathan M Davis newsgroup.d at jmdavisprog.com
Thu Dec 13 13:47:34 UTC 2018


On Thursday, December 13, 2018 4:41:01 AM MST aliak via Digitalmars-d wrote:
> On Tuesday, 11 December 2018 at 22:32:45 UTC, Jonathan M Davis
>
> wrote:
> > Ultimately, allocation is the main difference here. Nullable
> > provides a way to emulate the behavior of a pointer with
> > regards to nullability without having to allocate on the heap.
> > An Optional or Maybe type is ultimately the same thing, just
> > with a different name. If it weren't for the issue of heap
> > allocation, it could easily argued that pointers negate the
> > need for any kind of Nullable/Optional/Maybe type, because they
> > provide that functionality. And they don't even cause memory
> > safety issues if you're not doing pointer arithmetic. So,
> > really, I think that the need for heap allocation is _exactly_
> > the issue here that these types are designed to solve and that
> > without that, a Nullable/Optional/Maybe type isn't adding much.
>
> This is not the point of optional types. They're just there to
> denote if a value exists or not.

Which you can do with a pointer. There is no need to have an "optional" type
if you don't care about heap allocations. If you don't care about heap
allocations, than you can just use T* and not bother with creating a library
type. It even works if you already have a pointer, because you can have a
pointer to a pointer: T**. Having an optional/maybe type allows you to
indicate whether a value is present _without_ that extra heap allocation. It
gives you pointer semantics with regards to whether a value is present
without needing a pointer. So, ultimately, avoiding a heap allocation is the
reason that such optional/maybe types exist, and it's why Nullable exists.

> > At most, it's making it clear that it's expected that the value
> > can be null/empty/missing, because not all functions that
> > involve pointers consider null to be an acceptable value.
>
> That makes a big difference. With everything. Writing the code,
> maintaining code, reviewing code, coming back to code, getting in
> to a language. Principle of least surprises. Intuitiveness. These
> all matter:
>
> class C {}
> struct S {}
>
> * What is Nullable!(int*)? An int * that can be null? But wait...
> * What is Nullable!C? A reference type that can be null? Isn't
> that just "C" ? What is this adding? How does this make sense?
> * What does this do: "Nullable!C a = null; a.isNull;"? if it's
> false does that mean C exists? So C exists?
> * What is Nullable!S? A struct that can be null or a struct that
> may exist? Or was it just to avoid heap allocation?
> * What's the difference between null and any of those other
> types' isNull?
> * Nullable!C a = null; a.get; // should that throw/assert?
>
> These are all non-obvious.

The behavior is completely obvious if you understand that whether a Nullable
is null refers to the Nullable itself and not the value that it contains.
Looking at the documentation, it does need to be improved so that that is
clearer. But if you understand that, then there is no confusion.

And if you're not writing generic code, then the only reason to even put a
pointer or reference in a Nullable in the first place is so that you can
differentiate between a pointer or reference that has been given a value
rather than being simply default-initialized. If you didn't care about that,
then you would just use the pointer or reference directly, because the
Nullable would do _nothing_ for you. It would be a pointless wrapper.

And if you're writing generic code, then having isNull act differently for
pointers than it does for value types would not work at all, because you'd
have to special-case the code to handle the fact that assigning the Nullable
a value could still result in isNull being true in the case where the
Nullable contained a pointer or reference. So, the code would no longer be
generic. And if it wasn't going to be generic, then you'd just use the naked
pointer or reference rather than using a Nullable. The entire reason that
Nullable was changed to allow types that are naturally nullable was so that
generic code could use Nullable without having to be special-cased for
pointers or references. That does come with the downside that isNull might
be confusing if you don't understand what Nullable is, and the documentation
clearly needs to be improved on that front, but there's no problem there
with how Nullable works. It's just now a worse name, because it was changed
to allow types that are naturally nullable and that can therefore have the
value of null.

> > IMHO, the only real naming issue that we have with Nullable is
> > that once it was changed to allow actual pointers (whereas
> > originally, it just contained types that could not themselves
> > be null), the property isNull became confusing to some folks,
> > because they thought that the null value of a pointer and
> > whether the Nullable itself was null were related, when they're
> > not (and if they were, it would cause subtle bugs - especially
> > in generic code).
>
> Yes, and this is just bad. Bad bad bad. How can any API that
> causes subtle bugs be something anyone would want to keep around??

How on earth does Nullable cause subtle bugs? The only issue with it that I
see is that the fact that it's aliased to its get member risks bugs when you
change code that used a T to use Nullable!T. Without that alias, you'd be
forced to update the code and make sure that it could handle when isNull was
true, whereas right now, it can be trivial to just change a variable's type
from T to Nullable!T and assume that it works, and even if you're careful
and try to make the code use get and isNull properly, you can easily miss a
place where the alias is used and end up with a bug. But if the alias is
removed like the OP wants, then that entire problem goes away, and then I
don't see why Nullable would cause subtle bugs.

I'll grant you that now that Nullable allows types which are naturally
nullable, the name is worse, because it does sometimes cause confusion
around isNull. But it's against current policy to rename stuff in Phobos
simply to give it a better name, and if the documentation is improved, that
problem should largely go away. At that point, the only folks who would be
confused are those that don't read the docs, and folks who don't read the
documentation for the code they're reading or maintaining are going to be
confused in general. If we were to start over, then yeah, it should probably
be named Optional or Maybe or whatever rather than Nullable given that it
accepts naturally nullable types, but I fail to see how its design causes
bugs aside from the problems caused by the alias. And we should be able to
fix that via deprecation, only breaking code that arguably should be broken
in the process, whereas renaming stuff breaks _all_ code using Nullable, the
vast majority of which is perfectly fine - which is why Walter and Andrei
are against allowing renaming stuff. It breaks tons of code, and in general,
the benefit is highly subjective if not outright negligible. It's probably
worth more in this case than in many other cases, but it would still involve
breaking tons of perfectly valid code.

- Jonathan M Davis





More information about the Digitalmars-d mailing list