17

In F# mantra there seems to be a visceral avoidance of null, Nullable<T> and its ilk. In exchange, we are supposed to instead use option types. To be honest, I don't really see the difference.

  • My understanding of the F# option type is that it allows you to specify a type which can contain any of its normal values, or None. For example, an Option<int> allows all of the values that an int can have, in addition to None.

  • My understanding of the C# nullable types is that it allows you to specify a type which can contain any of its normal values, or null. For example, a Nullable<int> a.k.a int? allows all of the values that an int can have, in addition to null.

What's the difference? Do some vocabulary replacement with Nullable and Option, null and None, and you basically have the same thing. What's all the fuss over null about?

Ben Walker
  • 2,037
  • 5
  • 34
  • 56
Peter Olson
  • 139,199
  • 49
  • 202
  • 242
  • 2
    It's about avoiding logic errors as early as possible -- with `option`, you get a compiler warning/error for failing to check for `None`; with `null` you don't. – ildjarn Jun 21 '12 at 21:15
  • So in theory if there was a C# with pattern matching that ensured that you handled the `null` case, it would be a wash? – Peter Olson Jun 21 '12 at 21:17
  • IMO, pretty much, though others would argue otherwise about the _semantic_ sensibility of `null`. – ildjarn Jun 21 '12 at 21:19
  • Related: http://stackoverflow.com/questions/947003/f-why-arent-option-types-compatible-with-nullable-types – Daniel Jun 21 '12 at 21:29

6 Answers6

21

F# options are general, you can create Option<'T> for any type 'T.

Nullable<T> is a terrifically weird type; you can only apply it to structs, and though the Nullable type is itself a struct, it cannot be applied to itself. So you cannot create Nullable<Nullable<int>>, whereas you can create Option<Option<int>>. They had to do some framework magic to make that work for Nullable. In any case, this means that for Nullables, you have to know a priori if the type is a class or a struct, and if it's a class, you need to just use null rather than Nullable. It's an ugly leaky abstraction; it's main value seems to be with database interop, as I guess it's common to have `int, or no value' objects to deal with in database domains.

Im my opinion, the .Net framework is just an ugly mess when it comes to null and Nullable. You can argue either that F# 'adds to the mess' by having Option, or that it rescues you from the mess by suggesting that you avoid just null/Nullable (except when absolutely necessary for interop) and focus on clean solutions with Options. You can find people with both opinions.

You may also want to see

Best explanation for languages without null

Community
  • 1
  • 1
Brian
  • 117,631
  • 17
  • 236
  • 300
  • 1
    In answer to _why_ F# adds `option<_>` it's worth emphasizing that `Nullable` is deeply tied to `null`, which F# attempts to remove from consideration. – Daniel Jun 21 '12 at 21:41
  • 1
    @Daniel, and also, `option` comes from ML/OCaml, and F# was born of OCaml-compat. But yes, for the most part, F# tries to relegate null only to '.Net interop' scenarios. – Brian Jun 21 '12 at 21:48
9

Because every .NET reference type can have this extra, meaningless value—whether or not it ever is null, the possibility exists and you must check for it—and because Nullable uses null as its representation of "nothing," I think it makes a lot of sense to eliminate all that weirdness (which F# does) and require the possibility of "nothing" to be explicit. Option<_> does that.

Daniel
  • 47,404
  • 11
  • 101
  • 179
7

What's the difference?

F# lets you choose whether or not you want your type to be an option type and, when you do, encourages you to check for None and makes the presence or absence of None explicit in the type.

C# forces every reference type to allow null and does not encourage you to check for null.

So it is merely a difference in defaults.

Do some vocabulary replacement with Nullable and Option, null and None, and you basically have the same thing. What's all the fuss over null about?

As languages like SML, OCaml and Haskell have shown, removing null removes a lot of run-time errors from real code. To the extent that the original creator of null even describes it as his "billion dollar mistake".

J D
  • 48,105
  • 13
  • 171
  • 274
  • It may merely be a difference in defaults, but it's a huge practical difference: once an `Option` is reduced to `T`, it is always a `T`: the value can never be `None`. This lets you wall off the areas of code where the value could be `None` from those where it cannot. Contrast C#, where defensive coding demands that you check each reference for `null` before trying to use it. Your function's header comment can say "never pass `null` to this function", but that doesn't stop callers from passing `null` anyway. In F#, if you don't declare a parameter as `Option`, you cannot pass `None`. – Warren Young Dec 19 '18 at 20:50
  • @WarrenYoung: True but F# has a lot of caveats where that breaks down that other languages don't have. F# supports OOP so you can invoke methods on values, unless your value happens to be represented by `null` (e.g. `[]` and `None`) in which case methods fail with null reference exceptions. The REPL is broken for this reason, failing to pretty print values with nulls in them correctly. You have functions like `Array.zeroCreate` that create arrays of nulls when your type isn't supposed to support `null`. And so on. – J D Dec 20 '18 at 11:20
4

The advantage to using option is that it makes explicit that a variable can contain no value, whereas nullable types leave it implicit. Given a definition like:

string val = GetValue(object arg);

The type system does not document whether val can ever be null, or what will happen if arg is null. This means that repetitive checks need to be made at function boundaries to validate the assumptions of the caller and callee.

Along with pattern matching, code using option types can be statically checked to ensure both cases are handled, for example the following code results in a warning:

let f (io: int option) = function
| Some i -> i
Lee
  • 142,018
  • 20
  • 234
  • 287
2

As the OP mentions, there isn't much of a semantic difference between using the words optional or nullable when conveying optional types.

The problem with the built-in null system becomes apparent when you want to express non-optional types.

In C#, all reference types can be null. So, if we relied on the built-in null to express optional values, all reference types are forced to be optional ... whether the developer intended it or not. There is no way for a developer to specify a non-optional reference type (until C# 8).

So, the problem isn't with the semantic meaning of null. The problem is null is hijacked by reference types.

As a C# developer, i wish I could express optionality using the built-in null system. And that is exactly what C# 8 is doing with nullable reference types.

Matt Newcomb
  • 101
  • 5
0

Well, one difference is that for a Nullable<T>, T can only be a struct which reduces the use cases dramatically.

Also make sure to read this answer: https://stackoverflow.com/a/947869/288703

Community
  • 1
  • 1
Christoph
  • 26,519
  • 28
  • 95
  • 133