December 15, 2018
On Sat, 15 Dec 2018 09:11:37 +0000, aliak wrote:
> In generic code if I get a Nullable!T* I know I need to check whatever get returns is null or not. With any other T which could be a reference type it complicates my code.

You use Nullable!(T*) only when you need to distinguish "a null value that has been set" from "no value because it has not been set". In generic code, this is often when you don't know whether null is a valid value -- perhaps you don't even check whether you're dealing with something that can be null.
December 16, 2018
On Saturday, 15 December 2018 at 15:44:25 UTC, Jonathan M Davis wrote:
> It's quite possible for the code to simply be passing it around. And if you want a simple case where it would make sense to call get on a Nullable after assigning it a value, then consider a struct that has member that's a Nullable. A member function could then have code like
>
> auto foo(T t)
> {
>     ...
>     if(member.isNull)
>         member = t;
>     bar(member.get);
>     ...
> }
>
> If T were a class or pointer, and t were null, then that code would fail the assertion in get if nullable types were treated differently.

Yes. You're accessing get without checking isNull first. Bad code, bad practice, should not pass code review. Not sure I see how that's a bad thing.

Sounds like you want a type that's called HasBeenAssignTo. Not an optional type.

Where is this code that accesses get without checking isNull first? I've never seen it and it just sounds like bad code.

> On the other hand, any code that is generically passing values around and uses Nullable is going to have serious problems if Nullable treats pointers differently whenever it's given a value that's null.

What serious problems? Asserting when not checking isNull before calling .get? Hardly a serious problem.

>
> So, I suppose that an argument could be made that in the case where null is not a valid value, Nullable could treat a null value as the same as not having been given a value, but in any situation where null is a valid value, that does not work at all. And if the code is not generic, having Nullable use a separate bool can be extremely useful, whereas having it treat null the same as not having a value would make Nullable useless for pointers, because its semantics would be the same as if you used the pointer directly.

And how is null a valid value for a class?

>
> Nullable!(T*) is basically the same as T**. All of this arguing about trying to make Nullable treat pointers differently is like arguing that T** is useless, because T* can already be null. That extra level of indirection means something and can have real value. In the cases where it doesn't, you just don't use that extra level of indirection, but making it so that that extra level of indirection isn't possible reduces what you can do.

Not arguing to change T*. Like I already said I agree with you when it comes to raw pointers.

Anyway, it's fine if Nullable doesn't change. It is what it is, which is a wrapper type that tells you if something was assigned to. It's usage as an optional is quite lacking though. No bind function, and even if there was it'd have to special case if because Nullable!Class = null is a valid value. You can't map it because it's not a range. You're left with all the same holes as using raw pointers to check for existence with no compile time enforceable checks. And you have to special case your Nullable if you want to call functions on the T that Nullable is wrapping. Which is actually a Much bigger use case for optionals (i.e. optionals that wrap actual types that you want to perform operations on).

Cheers,
- Ali
December 17, 2018
On Sunday, 16 December 2018 at 23:13:32 UTC, aliak wrote:
> On Saturday, 15 December 2018 at 15:44:25 UTC, Jonathan M Davis wrote:
>> auto foo(T t)
>> {
>>     ...
>>     if(member.isNull)
>>         member = t;
>>     bar(member.get);
>>     ...
>> }
>>
>> If T were a class or pointer, and t were null, then that code would fail the assertion in get if nullable types were treated differently.
>
> Yes. You're accessing get without checking isNull first. Bad code, bad practice, should not pass code review. Not sure I see how that's a bad thing.

Agreed. Normally one would do either:

member.orElse(t).map!(bar); // or
member.withDefault(t).map!(bar);

> Sounds like you want a type that's called HasBeenAssignTo. Not an optional type.

Yep, that seems to be the source of our differences. My idea of Nullable has always been that it is exactly like Optional, but it is not.

It might be a good idea to update the docs to reflect that.
December 17, 2018
Oops...

When creating a PR to update the docs on Nullable, I was checking the semantics of Scala's optional type.

From what I can gather there was a lot of discussion in the Scala community about the very same topic:

---
var a: Option[String] = Some(null)
a.isDefined   // returns true!
---

They settled for this because Scala is build on top of Java and code written in Java could use the null value to signify something other than None. Often something like "present, but uninitialised".

It is something that they very much like to see different, and a lot of Scala developers cringe when they see it. Also, they have the Option constructor that corresponds with the semantics aliak and I argued for:

---
var a: Option[String] = Option(null)
a.isDefined    // return false!
---

I am slowly understanding why you would need some version of Nullable where isNull needs to return true for null values. It is for that uncommon case where null has semantics beyond not being a value. For instance, a library could use null to signify that a key is present but has no values assigned to it. Or some code could assign a difference between an array with a null item and an empty array.

---
[null].head() == Nullable!T(null);
[].head() == Nullable!T.init;
---

Thanks for your perseverance Jonathan :)
December 17, 2018
On Monday, 17 December 2018 at 09:39:12 UTC, Sebastiaan Koppe wrote:
> Oops...
>
> When creating a PR to update the docs on Nullable, I was checking the semantics of Scala's optional type.
>
> From what I can gather there was a lot of discussion in the Scala community about the very same topic:
>
> ---
> var a: Option[String] = Some(null)
> a.isDefined   // returns true!
> ---
>
> They settled for this because Scala is build on top of Java and code written in Java could use the null value to signify something other than None. Often something like "present, but uninitialised".
>
> It is something that they very much like to see different, and a lot of Scala developers cringe when they see it. Also, they have the Option constructor that corresponds with the semantics aliak and I argued for:
>
> ---
> var a: Option[String] = Option(null)
> a.isDefined    // return false!
> ---
>
> I am slowly understanding why you would need some version of Nullable where isNull needs to return true for null values. It is for that uncommon case where null has semantics beyond not being a value. For instance, a library could use null to signify that a key is present but has no values assigned to it. Or some code could assign a difference between an array with a null item and an empty array.
>
> ---
> [null].head() == Nullable!T(null);
> [].head() == Nullable!T.init;
> ---
>
> Thanks for your perseverance Jonathan :)

Yeah, that scala thing :( - you can guess which side of the argument I'm on :p Java null mixed in with scala optionals has bitten me numerous times - e.g. trying to get JSON data out of jackson. Most crashes have been because of java to scala and/or kotlin interop and because of java nulls. There was one SDK we were working on - kotlin version and swift version. Guess which one basically didn't crash, and guess which one crashed because of java nulls hiding in kotlin nullables.

I remember at some point swift allowed the same thing. And again, it was just a source of bugs. I don't think you can do that now in swift unless you really mess with reflection APIs and the objective-c runtime. But I'm not sure.

Anyway, I do agree that you need a way to represent null if it's a valid value. And IMO, null is a valid pointer value which is why I also argue you can't use pointers as optionals since null can be a valid value. And the cool thing about D is that we have pointers and classes. And I think an optional of T* should be sufficient for the uncommon use-cases you speak of (see: https://run.dlang.io/is/qh2OdQ).

I'm still not sure why you would want that with D classes 🤷‍♂️ But maybe there's code out there that does, which would be great to look at to see why it is doing that.

Cheers,
- Ali



1 2 3 4 5
Next ›   Last »