Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are references non-nullable by default in F#?

I know that in C# it isn't, and it is in languages like Haskell (if I am not wrong), so thought maybe F# also had the same semantics by default.

Also even though it doesn't exist in C#, it's a limitation of the language not the runtime, right? Like either F# or some other new .NET language can in fact implement this as a default without using any sort of hack.

like image 549
Joan Venge Avatar asked Mar 07 '11 20:03

Joan Venge


2 Answers

In F#, if you define a new class or other type in F# code, then it will be non-nullable by default, in the sense that e.g.

type MyClass() = ...
...
let x : MyClass = null   // does not compile

However it compiles down to .NET IL code as a class, which is a reference type on .NET, which can be null, and thus either C# could create nulls of that type, or even in F#

let x : MyClass = Unchecked.defaultOf<MyClass>

would give you a null. So in that sense, it is very much a "limitation of the runtime" - you can never create a .NET language that can both "expose a class to C# so that it looks like a normal class" and also "ensure that instances of that type are never null". So you always have to make pragmatic decisions here. F# tries to prevent you from accidents and the annoyance of dealing with null when you stay inside F#, but if you deal with interop or .NET runtime details, at the end of the day null is always hanging around. (Billion dollar mistake.)

like image 73
Brian Avatar answered Sep 22 '22 23:09

Brian


In addition to the points Brian raises, it is probably worth mentioning that when you define a new type in F# you can opt to allow null values if that's the behavior that you want. This is done via the AllowNullLiteral attribute:

[<AllowNullLiteral>]
type T1() = class end

type T2() = class end

let t1 : T1 = null
let t2 : T2 = null // compiler error

EDIT

Regarding your follow-up question, yes, this attribute applies to the type itself. The vast majority of F# types will not have this attribute applied; if you have a function or method with a parameter which you want to allow to have either a valid value of the type or a "null-like" sentinel value, then you would use an option type:

let myFunc (o:option<T2>) = 
  match o with
  | None -> "No value passed in"
  | Some(t2) -> "A T2 instance was passed in"

Option types are similar to nullable types in C#, except that they aren't limited to wrapping structs (and some additional minor differences irrelevant to this topic).

like image 37
kvb Avatar answered Sep 26 '22 23:09

kvb