Together with C#, F# will be incorporating nullability as a concept for .NET reference types. From the F# perspective, this fits with the default non-nullness of other F# types. But compatibility with existing code makes designing this a wild ride!
4. Null itself is perfectly valid
It really is!
You need to represent ”nothing” in a programming language
null accomplishes the task of representing “nothing”
13. Main goals
Distinguish between nullable and non-nullable reference types in the F# type system
Make nullable reference types less desirable to use than non-nullable reference types
Offer helper functionality and patterns for dealing with nulls
Offer compiler analysis for unsafe access of nullable reference types
17. Generic constraints
‘T when ‘T: null
‘T when ‘T: struct
‘T when ‘T: not struct
‘T when ‘T: not null
New!
Semantics:
• Error if the constraint is instantiated with an
F# type that has null as a proper value
• Warning if constraint is instantiated with a
nullable reference type
18. Pattern matching (majority case)
NonNull pattern can be used
anywhere, not just at the end
‘null’ pattern MUST be the first
for this ‘s’ to be non-null, as
per existing rules
19. Pattern matching (column-based)
s1 and s2 are still nullable!
Typechecking proceeds before
column analysis
May need to use NonNull
patterns to handle this
20. F# type relations with nullability
Type equivalence (x = y; y <> z)
◦ Nullness ignored, but warning given on mismatch
Type subsumption (B a subtype of A)
◦ Nullness ignored, but warning given on mismatch
Method overload resolution
◦ Nullness ignored, but warning given on mismatches in arguments and return types
Abstract slot inference
◦ Nullness ignored, but warning given on slot mismatches
Duplicate method checking
◦ Nullness is completely ignored
21. Type inference and mixing null/non-null
• First element in each collection is a non-null string
• Introduction of nullable string introduces type equivalence mismatch
• Solution is to add explicit type annotation
22. Type inference and nullability assertions
We cannot associate any arbitrary bool value with
nullability. A nullability assertion is needed.
23. Nullability and obj
Nullability is completely ignored for ‘obj’ type
The ‘obj’ type is used when type information is thrown out
Making this nullable/non-nullable is pointless considering throwing out type information
24. Null analysis for nullable value types
Partial alignment of existing nullable value types is possible
◦ Same syntax may be possible (int?, float?, etc.)
◦ Same or similar rules for type relations and type inference
27. Attributes to affect compiler analysis
[<NotNullWhenTrue>] and [<NotNullWhenFalse>]
◦ Used in bool-returning functions or methods that check for null
[<EnsuresNotNull>]
◦ Used on functions or methods that throw an exception when null is encountered
[<AssertsTrue>] and [<AssertsFalse>]
◦ Used for asserting nullability, such as in test frameworks, and avoiding warnings
28. [<NonNullTypes>] attribute
Three cases:
• NonNullTypes(true)
• Nullability is a concept
• NonNullTypes(false)
• Nullability is not a concept
• No attribute at all
• Null oblivious semantics
30. Three choices involved
Distinguish between nullable and non-nullable reference types
Do not distinguish between nullable and non-nullable reference types
Null oblivious (i.e., no assumption in either direction)
31. What is null oblivious for F#?
No information about nullability for reference types in a given scope
If an oblivious value is given a nullable reference type, it is nullable
If an oblivious value is given a non-nullable reference type, it is non-nullable
Interesting question: What if ‘s’ is not annotated?
32. The case for null oblivious
Pros:
◦ Less warnings (if you don’t want them)
◦ Makes porting existing code a lot easier
◦ Logically consistent
Cons:
◦ Less warnings (if you do want them)
◦ More potential NREs
◦ Entire programs can be oblivious due to type inference
33. The case for assuming nullability
Pros:
◦ More warnings (if you want them)
◦ Less NREs if you account for the warnings
◦ Avoids completely oblivious programs
Cons:
◦ More warnings (if you don’t want them)
◦ Not logically consistent – no information means no assumptions, so why make them?
34. There is no perfect approach
Some users may prefer obliviousness
Some users may prefer assuming nullability
Getting it right will be a challenge
Current design is not set in stone (yet!)
38. Compiled form of F# options
• F# options compile to null for None case
• Changing this would be a massive source-
breaking change
• Ironically, F# options would be interpreted as
nullable reference types
• No good resolution to this quite yet
39. Why do this feature?
WAXING PHILOSOPHICAL ON NULLS