United Kingdom: +44 (0)208 088 8978

F# Nullness support

Isaac shows up a preview of the work being done on the F# compiler to support .NET's nullness capabilities.

We're hiring Software Developers

Click here to find out more

With regards to nullability, F#'s design has always been to simply disallow nulls within the type system. Instead, you use the option type to signify potential "absence of a value", with compiler support to prevent you unsafely accessing non-existent data:

type Person = { Name : string; Age : int }

let x : Person = null // compiler error
let y : Person option = None

y.Name // compiler error

match y with
| Some person -> $"Hello, {person.Name}!"
| None -> "I don't know who you are."

For everyday coding, this works remarkably effectively. However, F# does not currently prevent nullable assignment or accessors to any reference types defined outside of F#. This includes all classes within the .NET Framework Class Library (FCL) - including core types such as System.String:

let p = { Name = null; Age = 21 } // allowed
p.Name.Length // null reference exception!

C# has addressed this problem in more recent versions through the use of flow analysis and type annotations (attributes). These attributes have now been pushed through the entire FCL, and F# is now being enhanced to take advantage of them in this pull request. This pull request is still in draft, but it's interesting to see the progression of it, and how it will start to affect everyday F# code. Let's take a look at it in some more detail:

Firstly, code like this will give warnings (which can - and should - be treated as errors):

let cantBeNull : string = null // FS3261 string does not support null

Instead, you will need to annotate types with the new | null qualifier to make a "nullable" string, and will not be able to access members on nullable reference types without warnings / errors:

let canBeNull : string | null = null // permitted
let canAlsoBeNull : string | null = "test" // also permitted, analogous to (Some "test")
canAlsoBeNull.Length // Gives FS3261 warning

The compiler will respect all nullness annotations built-into the FCL:

let textInput = System.Console.ReadLine() // string | null

let di = DirectoryInfo "test"
let diName = di.Name // string
let parentDi = di.Parent // string | null

As expected, method and function inputs / outputs will also be respected:

let foo (x:string) = 123
foo textInput // FS3261

You'll be able to use pattern matching to safely "unwrap" a nullable reference value, using either keywords or active patterns:

let pmActivePattern =
    match textInput with
    | Null -> "a blank string"
    | NonNull textInput -> textInput

let pmNull =
    match textInput with
    | null -> "a blank string"
    | textInput -> textInput // automagically unwrapped as a non-nullable ref type

Here's a quick screenshot just to illustrate the above samples working in VS2022 using a preview of the compiler:


There's still clearly more work to be done on this feature, and some questions to resolve such as the future of the Option.ofObj and toObj functions, but it's shaping up very nicely. I'm looking forward to this feature "closing out" the last part of "null unsafety" within F#!