Live data from Hacker News

Why nullable types?

medium.com

91–100 of 119 posts

Re: Why nullable types?

#91
I think saying that Option types are different from nullable types is not true. They differ at the semantic level, around how you can interact with them (one requires you to case on whether or not the value is there, the other requires you to do that with an if statement), but at the type level the describe the same construct.

I think saying something is not the same as something else, when evidently the difference can to reduced down to syntactic sugar is a bad way categorizing and differentiating types.

Re: Why nullable types?

#92

I sometimes wonder if the problem is that we don't have enough nulls. null is typically used as a flag value, but the meaning can be ambiguous: maybe it's the absence of a value, maybe an error occured, etc. Sometimes it has more than one meaning for the same type. Maybe types should be allowed to declare multiple nulls (effectively like an Enum in java) for different flag values. Operations on the different nulls wo…

Completely reasonable request, easy to implement along the lines of: public class Name { private Name(String name, int flag){...} public static Name asMissing(){new Name(null, -1);} public static Name asNotApplicable(){new Name(null, -2);} public static Name asNotAvailableYet(){new Name(null, -3);} public static Name asForbidden(){new Name(null, -4);} public static Name create(String name){new Name(name, 0)} public b…

Yep. I'd prefer enum-like syntactic sugar, something like:

   public class Name {
        private Name(String name){...}
    
        flags {
          MISSING, NOT_APPLICABLE, NOT_AVAILABLE_YET, FORBIDDEN
        }
        ...
    }
In particular, you shouldn't have to specify a constructor, since these are flag values that don't necessary have any state associated with them.

Re: Why nullable types?

#93
post #24

I agree that union-typed null was the best solution for Dart, but I think many of the article's criticisms of option types aren't good. > We can’t perform arithmetic on an Option any more than we could on a List . Why not? One sensible definition is Some(x) + Some(y) = Some(x + y) _ + _ = None() > In fact, with Dart, we’ve found that most existing code is already statically null safe This is a very good reason to use…

> Different people will assign different meanings to null, and it's a real headache when trying to write generic code. This is a legitimate concern, but my experience has been that it is a surprisingly rare problem in practice. We software engineers are trained to believe that any system that supports 1 of something is obligated to generalize and support N of them. But you'd be surprised how far just one sentinel val…

The bigger problem in generic code is that flattening means that you can't distinguish between incoming nullable values (that you don't know are nullable, because it's a type parameter!) that are null, and nulls in your own code.

Re: Why nullable types?

#94
post #45
post #24

I agree that union-typed null was the best solution for Dart, but I think many of the article's criticisms of option types aren't good. > We can’t perform arithmetic on an Option any more than we could on a List . Why not? One sensible definition is Some(x) + Some(y) = Some(x + y) _ + _ = None() > In fact, with Dart, we’ve found that most existing code is already statically null safe This is a very good reason to use…

I agree with all your other points but > > We can’t perform arithmetic on an Option any more than we could on a List . > Why not? One sensible definition is (...) It's not the only sensible definition. It would be equally sensible to implement it as: Some(x) + Some(y) => Some(x+y) Some(x) + None => Some(x) None + Some(y) => Some(y) None + None => None

It really depends on what the meaning of None is. OP's definition makes perfect sense if it means "unknown". Yours make sense if it means "zero", but that's far less useful. If it means "missing" - which is what the article says they wanted - then any operation involving None should be an error (but Some can still be handled automatically).

It can be interesting to see what other languages do in this situation. For example, C# got nullable value types in version 2, and had to decide what to do with operators in a similar vein (including pre-existing overloaded operators in user code) - they called it "lifting":

https://docs.microsoft.com/en-us/dotnet/csharp/language-refe... https://docs.microsoft.com/en-us/dotnet/csharp/language-refe...

You might notice that it almost, but not quite, has consistent semantics: null means "unknown", which is particularly obvious from Booleans: (true | null) is true, and (false & null) is false, but (true & null) and (false | null) are both null. However, comparison operators aren't consistent: you'd expect x == null to be null if it meant "unknown", but the language will always give you either true or false; and ditto for relative comparisons. This last one means that it's possible for (x == y), (x y) to all be false.

What's interesting is that, in practice, it's rare to see reliance on any of this behavior in idiomatic C# code - "null" is usually used as "missing", not as "unknown", so all this magic is, at best, irrelevant, and at worst, actively harmful (because it defers a logic error).

Re: Why nullable types?

#95

I sometimes wonder if the problem is that we don't have enough nulls. null is typically used as a flag value, but the meaning can be ambiguous: maybe it's the absence of a value, maybe an error occured, etc. Sometimes it has more than one meaning for the same type. Maybe types should be allowed to declare multiple nulls (effectively like an Enum in java) for different flag values. Operations on the different nulls wo…

Most of those meanings don't make sense for the type itself to define, because it's the context where the type is used that determines whether something can be missing, or there can be an error etc. ADTs solve that.

Re: Why nullable types?

#96
post #91

I think saying that Option types are different from nullable types is not true. They differ at the semantic level, around how you can interact with them (one requires you to case on whether or not the value is there, the other requires you to do that with an if statement), but at the type level the describe the same construct. I think saying something is not the same as something else, when evidently the difference c…

The article mentions unions vs discriminated unions and then mentions nesting nullable being different than nesting optional, but it doesn't really tie the knot.

Imagine (sorry for pseudocode):

  type Option T = Some T | None
  type Nullable T = T | null
The difference here is that Option "tags" each part of its union, which guarantees that the parts are disjoint. In Nullable, the parts of the union are disjoint only if T is not itself nullable. If T is S | null, Nullable T is S | null | null, which is just S | null (since null and null are not disjoint.

Re: Why nullable types?

#97

I guess a third option would be to use an "Option" but with much more syntactic sugar. So, rather than calling func(some(3)), you'd just call func(3) and the compiler would automatically wrap it up. Func would be declared this way: def func(int?) to indicate that it's an optional type. IMO you get the best of both world. And then, in many places in the code, you'd have to do "r?.foo()" if r is an Option type. It's di…

TypeScript and, AFAIK, Kotlin provide exactly that. The problem with that special syntax sugar is that then there's no monad to compose with. But it does feel nice for many use cases.

TypeScript also uses union types for null & undefined. But then on top of that, it has optionals. So you can have:

   function foo(x: number | undefined);
or you can have:

   function foo(x?: number);
The type of x inside foo is the same in both cases, but the type of foo differs. The first version can only be called as foo(x) - where x is possibly undefined - but the second one can also be invoked as foo(), which then has the same meaning as foo(undefined).

But this is mostly because they were trying to come up with a type system to capture existing JavaScript patterns. I doubt it'd look like that if it could be designed from scratch.

Re: Why nullable types?

#98
Optional and nullable types are very different. When you wrap a type T in Optional, you make a completely new type that just happens to contain T inside, which means you cannot use Optional in places where you previously accepted T, because you break a contract for the caller. With nullable types (which essentially represent a set `{t | t in T} v {NULL}`) it is possible. This is similar to the notions of co- and contra-variance for inputs/outputs of a function.

Rich Hickey had a good talk about problems of representing a missing information in programs, and why union types allow you to incrementally make changes in your codebase without refactoring the whole thing at once.

Re: Why nullable types?

#99

Earlier quoted context omitted.

Adding an `Option` type to an existing language doesnt really solve many problems unless you can also somehow remove it's existing support for `null. Java added `Optional` and while it makes interacting with newer API's clearer, there is nothing preventing you from passing a null Optional. I think if you are designing a language from scratch, you should avoid null, but nullable types are a good feature for existing l…

Language culture matters. Scala has both `Option` and `null`, but there's a strong culture of "`null` is only for Java interop", so you very rarely see a `null` in the wild from libraries, or wonder whether it'd be valid to pass it in.

Interestingly, Scala 3 is adding union types as well as explicit `null`s:

https://dotty.epfl.ch/docs/reference/other-new-features/expl...

I think that the expectation is that idiomatic Scala code will mostly still use `Option`, while `null` will be used for interoperability with Java, JavaScript or Scala libraries that happen to use `null`.

In any case, I am very much looking forward to these additions.

Re: Why nullable types?

#100

Earlier quoted context omitted.

Adding an `Option` type to an existing language doesnt really solve many problems unless you can also somehow remove it's existing support for `null. Java added `Optional` and while it makes interacting with newer API's clearer, there is nothing preventing you from passing a null Optional. I think if you are designing a language from scratch, you should avoid null, but nullable types are a good feature for existing l…

Language culture matters. Scala has both `Option` and `null`, but there's a strong culture of "`null` is only for Java interop", so you very rarely see a `null` in the wild from libraries, or wonder whether it'd be valid to pass it in.

Your username is so incredibly relevant here that I had to check if this was a novelty account.
Post reply on HN