Thread overview
Is Nullable supposed to provide Optional semantics?
Dec 29, 2017
vit
Dec 29, 2017
Dukc
Dec 29, 2017
vit
Dec 30, 2017
Jonathan M Davis
December 29, 2017
I've been bitten by trying to use Nullable(T) on class types. Minimal example...

import std.typecons : Nullable;

void main()
{
    auto o = new Object();
    o.toString();

    Nullable!Object n = o;
    o.toString();

    n.nullify();
    o.toString(); // SegV!
}

The SEGV is caused by nullify calling object.destroy() on o, invalidating it. This makes Nullable unsafe to use with class types when there may be other references to the optional values in the program.

Perhaps I'm not using the correct abstraction. I'm looking for something equivalent to java.util.Optional, or Haskell's Maybe.

If Nullable(T) *is* intended to be used like Java Optional, then calling .destroy when clearing the value seems to be going beyond its remit for class types.

For those confused as to why you'd want to wrap a Nullable around something that already has nullable semantics, it's mostly about making APIs that explicitly declare their optional return values. See: http://www.oracle.com/technetwork/articles/java/java8-optional-2175753.html

I also want to use it in template code that works with both value & reference types.

Chris.
December 29, 2017
On Friday, 29 December 2017 at 20:52:51 UTC, Chris Paulson-Ellis wrote:
> I've been bitten by trying to use Nullable(T) on class types. Minimal example...
>
> [...]

use:
   n = Nullable!Object.init;   //doesn't call destroy

instead of:
   n.nullify();
December 29, 2017
On Friday, 29 December 2017 at 21:34:27 UTC, vit wrote:
> use:
>    n = Nullable!Object.init;   //doesn't call destroy
>
> instead of:
>    n.nullify();

Only nullify() can make isNull return true again. I need that semantic.
December 29, 2017
On Friday, 29 December 2017 at 21:43:25 UTC, Chris Paulson-Ellis wrote:
> Only nullify() can make isNull return true again. I need that semantic.

Quick idea without much afterthought: instead of Nullable, use pointer to o?
December 29, 2017
On Friday, 29 December 2017 at 21:43:25 UTC, Chris Paulson-Ellis wrote:
> On Friday, 29 December 2017 at 21:34:27 UTC, vit wrote:
>> use:
>>    n = Nullable!Object.init;   //doesn't call destroy
>>
>> instead of:
>>    n.nullify();
>
> Only nullify() can make isNull return true again. I need that semantic.


    import std.typecons : Nullable;

    auto o = new Object();

    Nullable!Object n;
    assert(n.isNull == true);

    n = o;
    assert(n.isNull == false);

    n = Nullable!Object.init;
    assert(n.isNull == true);

    o.toString();       //OK

    assert(Nullable!Object.init.isNull == true);


more: https://forum.dlang.org/thread/jrdedmxnycbqzcprebjl@forum.dlang.org?page=1
December 30, 2017
On Friday, 29 December 2017 at 22:08:59 UTC, vit wrote:
>     n = Nullable!Object.init;
>     assert(n.isNull == true);
> [...]
> more: https://forum.dlang.org/thread/jrdedmxnycbqzcprebjl@forum.dlang.org?page=1

Thanks.

No-one in the linked thread seemed to know why .destroy is used in nullify. Looking at the commit history it used to be .clear, but maybe that did the same thing, I don't know.

I still think nullify is going beyond the Nullable remit for reference types. Unless anyone disagrees, I'll submit a bug report.

C.
December 30, 2017
On Saturday, December 30, 2017 08:59:40 Chris Paulson-Ellis via Digitalmars- d-learn wrote:
> On Friday, 29 December 2017 at 22:08:59 UTC, vit wrote:
> >     n = Nullable!Object.init;
> >     assert(n.isNull == true);
> >
> > [...]
> > more:
> > https://forum.dlang.org/thread/jrdedmxnycbqzcprebjl@forum.dlang.org?page
> > =1
> Thanks.
>
> No-one in the linked thread seemed to know why .destroy is used in nullify. Looking at the commit history it used to be .clear, but maybe that did the same thing, I don't know.

destroy used to be called clear (e.g. that's what TDPL calls it), but it was renamed, because it was too easily confused with clearing a container and tended to be misused.

- Jonathan M Davis

December 30, 2017
On 12/30/17 3:59 AM, Chris Paulson-Ellis wrote:
> On Friday, 29 December 2017 at 22:08:59 UTC, vit wrote:
>>     n = Nullable!Object.init;
>>     assert(n.isNull == true);
>> [...]
>> more: https://forum.dlang.org/thread/jrdedmxnycbqzcprebjl@forum.dlang.org?page=1 
>>
> 
> Thanks.
> 
> No-one in the linked thread seemed to know why .destroy is used in nullify. Looking at the commit history it used to be .clear, but maybe that did the same thing, I don't know.

clear was renamed to destroy because clear typically is used in containers to remove all elements (but not destroy the container itself). Since clear was a UFCS function, it led to confusion:

somestruct.clear; // equivalent to clear(somestruct), destroys it.
container.clear; // remove all elements
clear(container); // destroy the container

So destroy was used instead, and it's a much better name.

> I still think nullify is going beyond the Nullable remit for reference types. Unless anyone disagrees, I'll submit a bug report.

I think you are correct. I believe it is calling destroy expecting it to work like calling destroy on a pointer (which does nothing I think, or just sets it to null). But it doesn't work that way.

What may have been missed by the others is that the call to toString that caused the segfault was NOT on the nullable `n`, but on the original object `o`. So nullifying the nullable kills any other references to the same object.

Please file a bug report.

-Steve
January 13, 2018
On Saturday, 30 December 2017 at 19:11:05 UTC, Steven Schveighoffer wrote:
> Please file a bug report.

Sorry for the delay - stuff happened.

I reopened an existing bug that I found:
https://issues.dlang.org/show_bug.cgi?id=17440