Skip to main content
in a nutshell: nullable types break polymorphism
Source Link

Much of the angst over nulls are due to languages where every reference type is nullable by default. But this is not an issue for Typescript or C# 8 so lets disregard that.

There are two basic approaches to how to optional values are represented:

  • A distinct container type (the Option type) which contain zero or one instances of the actual value.

  • A union type of the original type with a "sentinel" value, null, which indicate the lack of a value. In Typescript it would be declared as a (actualType | null) type union. (Or shortened as actualType?, but the the explicit union makes it clearer what is going on.)

On the face of it they seem similar, but a significant difference is that containers nest but type unions don't. An option can contain another option as its value, but ((actualType | null) | null) is just the same as (actualType | null).

For example consider a dictionary. The lookup function could return an option: Nothing if the key does not exist, otherwise Some value. This will work with any type of value. If the values in the dictionary are themselves options, the lookup will just return an option where the value (if any) is itself an option.

But what if we instead use null to represent a missing value? Then the lookup function can return a value or a null, but there is no way to distinguish between if the null means the key did not exist or the key did exist but the associated value was null. You lose what could be important information.

Dictionaries are just an example, the problem arise anywhere you have a data-structure with multiple levels of optional elements. Nullable types prevent polymorphism: code can't manipulate data of an unknown type generically, it has to treat nullable and non-nullable types differently.

You can see how C# has to use awkward workarounds in the Dictionary interface. Either it throws an Exception (eww!) if the key is not found, or with TryGetValue() it returns a boolean indicating if the key is found and then the value (or null) on an out parameter. This is pretty ugly and it doesn't scale and is not composable. Option types solves this elegantly, and in a way that is consistent with the rest of the language.

Much of the angst over nulls are due to languages where every reference type is nullable by default. But this is not an issue for Typescript or C# 8 so lets disregard that.

There are two basic approaches to how to optional values are represented:

  • A distinct container type (the Option type) which contain zero or one instances of the actual value.

  • A union type of the original type with a "sentinel" value, null, which indicate the lack of a value. In Typescript it would be declared as a (actualType | null) type union. (Or shortened as actualType?, but the the explicit union makes it clearer what is going on.)

On the face of it they seem similar, but a significant difference is that containers nest but type unions don't. An option can contain another option as its value, but ((actualType | null) | null) is just the same as (actualType | null).

For example consider a dictionary. The lookup function could return an option: Nothing if the key does not exist, otherwise Some value. This will work with any type of value. If the values in the dictionary are themselves options, the lookup will just return an option where the value (if any) is itself an option.

But what if we instead use null to represent a missing value? Then the lookup function can return a value or a null, but there is no way to distinguish between if the null means the key did not exist or the key did exist but the associated value was null. You lose what could be important information.

Dictionaries are just an example, the problem arise anywhere you have a data-structure with multiple levels of optional elements.

You can see how C# has to use awkward workarounds in the Dictionary interface. Either it throws an Exception (eww!) if the key is not found, or with TryGetValue() it returns a boolean indicating if the key is found and then the value (or null) on an out parameter. This is pretty ugly and it doesn't scale and is not composable. Option types solves this elegantly, and in a way that is consistent with the rest of the language.

Much of the angst over nulls are due to languages where every reference type is nullable by default. But this is not an issue for Typescript or C# 8 so lets disregard that.

There are two basic approaches to how to optional values are represented:

  • A distinct container type (the Option type) which contain zero or one instances of the actual value.

  • A union type of the original type with a "sentinel" value, null, which indicate the lack of a value. In Typescript it would be declared as a (actualType | null) type union. (Or shortened as actualType?, but the the explicit union makes it clearer what is going on.)

On the face of it they seem similar, but a significant difference is that containers nest but type unions don't. An option can contain another option as its value, but ((actualType | null) | null) is just the same as (actualType | null).

For example consider a dictionary. The lookup function could return an option: Nothing if the key does not exist, otherwise Some value. This will work with any type of value. If the values in the dictionary are themselves options, the lookup will just return an option where the value (if any) is itself an option.

But what if we instead use null to represent a missing value? Then the lookup function can return a value or a null, but there is no way to distinguish between if the null means the key did not exist or the key did exist but the associated value was null. You lose what could be important information.

Dictionaries are just an example, the problem arise anywhere you have a data-structure with multiple levels of optional elements. Nullable types prevent polymorphism: code can't manipulate data of an unknown type generically, it has to treat nullable and non-nullable types differently.

You can see how C# has to use awkward workarounds in the Dictionary interface. Either it throws an Exception (eww!) if the key is not found, or with TryGetValue() it returns a boolean indicating if the key is found and then the value (or null) on an out parameter. This is pretty ugly and it doesn't scale and is not composable. Option types solves this elegantly, and in a way that is consistent with the rest of the language.

added 1422 characters in body
Source Link
JacquesB
  • 61k
  • 21
  • 133
  • 187

Nulls haveMuch of the angst over nulls are due to languages where every reference type is nullable by default. But this is not an issue for Typescript or C# 8 so lets disregard that.

There are two basic approaches to how to optional values are represented:

  • A distinct container type (the Option type) which contain zero or one instances of the actual value.

  • A union type of the original type with a "sentinel" value, null, which indicate the lack of a value. In Typescript it would be declared as a (actualType | null) type union. (Or shortened as actualType?, but the the explicit union makes it clearer what is going on.)

On the face of it they seem similar, but a significant downside indifference is that they can't be composedcontainers nest but type unions don't. An option can contain another option as its value, but ((actualType | null) | null) is just the same as (actualType | null).

ConsiderFor example consider a dictionary. The lookup function maycould return an option: Nothing if the key does not exist, otherwise Some value. This will work with any type of value. If the values in the dictionary are themselves options, the lookup will just returnsreturn an option where the value (if any) is itself an option.

But what if we instead use null to represent a missing value is indicated by a null? Then the lookup function can return a value or a null, but there is no way to distinguish between if the null means the key did not exist or the key did exist but the associated value was null. You lose what could be important information.

Dictionaries are just an example, the problem arise anywhere you have a data-structure with multiple levels of optional elements.

You can see how C# has to use awkward workarounds in the Dictionary interface. Either it throws an Exception (eww!) if the key is not found, or with TryGetValue() it returns a boolean indicating if the key is found and then the value (or null) on an out parameter. This is pretty ugly and it doesn't scale and is not composable. Option types solves this elegantly, and in a way that is consistent with the rest of the language.

Nulls have a significant downside in that they can't be composed.

Consider a dictionary. The lookup function may return an option: Nothing if the key does not exist, otherwise Some value. This will work with any type of value. If the values in the dictionary are themselves options, the lookup will just returns an option where the value (if any) is an option.

But what if a missing value is indicated by a null? Then the lookup function can return a value or a null, but there is no way to distinguish between if the null means the key did not exist or the key did exist but the associated value was null. You lose what could be important information.

Much of the angst over nulls are due to languages where every reference type is nullable by default. But this is not an issue for Typescript or C# 8 so lets disregard that.

There are two basic approaches to how to optional values are represented:

  • A distinct container type (the Option type) which contain zero or one instances of the actual value.

  • A union type of the original type with a "sentinel" value, null, which indicate the lack of a value. In Typescript it would be declared as a (actualType | null) type union. (Or shortened as actualType?, but the the explicit union makes it clearer what is going on.)

On the face of it they seem similar, but a significant difference is that containers nest but type unions don't. An option can contain another option as its value, but ((actualType | null) | null) is just the same as (actualType | null).

For example consider a dictionary. The lookup function could return an option: Nothing if the key does not exist, otherwise Some value. This will work with any type of value. If the values in the dictionary are themselves options, the lookup will just return an option where the value (if any) is itself an option.

But what if we instead use null to represent a missing value? Then the lookup function can return a value or a null, but there is no way to distinguish between if the null means the key did not exist or the key did exist but the associated value was null. You lose what could be important information.

Dictionaries are just an example, the problem arise anywhere you have a data-structure with multiple levels of optional elements.

You can see how C# has to use awkward workarounds in the Dictionary interface. Either it throws an Exception (eww!) if the key is not found, or with TryGetValue() it returns a boolean indicating if the key is found and then the value (or null) on an out parameter. This is pretty ugly and it doesn't scale and is not composable. Option types solves this elegantly, and in a way that is consistent with the rest of the language.

deleted 2 characters in body
Source Link
JacquesB
  • 61k
  • 21
  • 133
  • 187

Nulls have a significant downside in that they can't be composed.

Consider a dictionary. The lookup function may return an option: Nothing if the key does not exist, otherwise Some value. This will work with any type of value including options. If the values in the dictionary are themselves options, the lookup will just returns an option where the value (if any) is an option.

But what if a missing value is indicated by a null? Then the lookup function can return a value or a null, but there is no wasway to distinguish between if the null means the key did not exist or the key did exist but the associated value was null. You lose what could be important information.

Nulls have a significant downside in that they can't be composed.

Consider a dictionary. The lookup function may return an option: Nothing if the key does not exist, otherwise Some value. This will work with any type of value including options. If the values in the dictionary are options, the lookup will returns an option where the value (if any) is an option.

But what if a missing value is indicated by a null? Then the lookup function can return a value or a null, but there is no was to distinguish between if the null means the key did not exist or the key did exist but the associated value was null. You lose what could be important information.

Nulls have a significant downside in that they can't be composed.

Consider a dictionary. The lookup function may return an option: Nothing if the key does not exist, otherwise Some value. This will work with any type of value. If the values in the dictionary are themselves options, the lookup will just returns an option where the value (if any) is an option.

But what if a missing value is indicated by a null? Then the lookup function can return a value or a null, but there is no way to distinguish between if the null means the key did not exist or the key did exist but the associated value was null. You lose what could be important information.

Source Link
JacquesB
  • 61k
  • 21
  • 133
  • 187
Loading