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.
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.)
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).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With