You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/fsharp/language-reference/generics/constraints.md
+5Lines changed: 5 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -21,6 +21,7 @@ There are several different constraints you can apply to limit the types that ca
21
21
|----------|------|-----------|
22
22
|Type Constraint|*type-parameter* :>*type*|The provided type must be equal to or derived from the type specified, or, if the type is an interface, the provided type must implement the interface.|
23
23
|Null Constraint|*type-parameter* : null|The provided type must support the null literal. This includes all .NET object types but not F# list, tuple, function, class, record, or union types.|
24
+
|Not Null Constraint|*type-parameter* : not null|The provided type must not support the null literal. This includes both `null` annotated types and types which have null as their representation value (such as the option type).|
24
25
|Explicit Member Constraint|[(]*type-parameter*[or ... or *type-parameter*)] : (*member-signature*)|At least one of the type arguments provided must have a member that has the specified signature; not intended for common use. Members must be either explicitly defined on the type or part of an implicit type extension to be valid targets for an Explicit Member Constraint.|
25
26
|Constructor Constraint|*type-parameter* : ( new : unit -> 'a )|The provided type must have a parameterless constructor.|
26
27
|Value Type Constraint|*type-parameter* : struct|The provided type must be a .NET value type.|
@@ -54,6 +55,10 @@ type Class2<'T when 'T :> System.IComparable> =
54
55
type Class3<'T when 'T : null> =
55
56
class end
56
57
58
+
// Not Null constraint
59
+
type Class3<'T when 'T : not null> =
60
+
class end
61
+
57
62
// Member constraint with instance member
58
63
type Class5<'T when 'T : (member Method1 : 'T -> int)> =
Copy file name to clipboardExpand all lines: docs/fsharp/language-reference/values/null-values.md
+65-1Lines changed: 65 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -7,7 +7,7 @@ ms.date: 08/15/2020
7
7
8
8
This topic describes how the null value is used in F#.
9
9
10
-
## Null Value
10
+
## Null Values Prior To F# 9
11
11
12
12
The null value is not normally used in F# for values or variables. However, null appears as an abnormal value in certain situations. If a type is defined in F#, null is not permitted as a regular value unless the [AllowNullLiteral](https://fsharp.github.io/fsharp-core-docs/reference/fsharp-core-allownullliteralattribute.html#Value) attribute is applied to the type. If a type is defined in some other .NET language, null is a possible value, and when you are interoperating with such types, your F# code might encounter null values.
13
13
@@ -31,6 +31,70 @@ You can use the following code to check if an arbitrary value is null.
In F# 9, extra capabilities are added to the language to deal with reference types which can have `null` as a value. Those are off by default - to turn them on, the following property must be put in your project file:
37
+
38
+
```xml
39
+
<Nullable>enable</Nullable>
40
+
```
41
+
42
+
To explicitly opt-in into nullability, a type declaration has to be suffixed with the new syntax:
43
+
44
+
```fsharp
45
+
type | null
46
+
```
47
+
48
+
The bar symbol `|` has the meaning of a logical OR in the syntax, building a union of two disjoint sets of types: the underlying type, and the nullable reference. This is the same syntactical symbol which is used for declaring multiple cases of an F# discriminated union: `type AB = A | B` carries the meaning of either `A`, or `B`.
49
+
50
+
The nullable annotation ` | null` can be used at all places where a reference type would be normally used:
51
+
52
+
- Fields of union types, record types and custom types.
53
+
- Type aliases to existing types.
54
+
- Type applications of a generic type.
55
+
- Explicit type annotations to let bindings, parameters or return types.
56
+
- Type annotations to object-programming constructs like members, properties or fields.
57
+
58
+
```fsharp
59
+
type AB = A | B
60
+
type AbNull = AB | null
61
+
62
+
type RecordField = { X: string | null }
63
+
type TupleField = string * string | null
64
+
65
+
type NestedGenerics = { Z : List<List<string | null> | null> | null }
66
+
```
67
+
68
+
The bar symbol `|` does have other usages in F# which might lead to syntactical ambiguities. In such cases, parentheses are needed around the null-annotated type:
69
+
70
+
```fsharp
71
+
// Unexpected symbol '|' (directly before 'null') in member definition
72
+
type DUField = N of string | null
73
+
```
74
+
75
+
Wrapping the same type into a pair of `( )` parentheses fixes the issue:
76
+
77
+
```fsharp
78
+
type DUField = N of (string | null)
79
+
```
80
+
81
+
When used in pattern matching, `|` is used to separate different pattern matching clauses.
82
+
83
+
```fsharp
84
+
match x with
85
+
| ?: string | null -> ...
86
+
```
87
+
88
+
This snippet is actually equivalent to code first doing a type test against the `string` type, and then having a separate clause for handling null:
89
+
90
+
```fsharp
91
+
match x with
92
+
| ?: string
93
+
| null -> ...
94
+
```
95
+
96
+
Note that all these capabilities were added to the language for the interoperability purposes. Using `| null` within F# code is not considered idiomatic for denoting missing information - for that purpose use options, as described above.
Copy file name to clipboardExpand all lines: docs/fsharp/style-guide/component-design-guidelines.md
+54-1Lines changed: 54 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -693,11 +693,64 @@ let checkNonNull argName (arg: obj) =
693
693
| null -> nullArg argName
694
694
| _ -> ()
695
695
696
-
let checkNonNull` argName (arg: obj) =
696
+
let checkNonNull' argName (arg: obj) =
697
697
if isNull arg then nullArg argName
698
698
else ()
699
699
```
700
700
701
+
Starting with F# 9, you can leverage the new ` | null`[syntax](../language-reference/values/null-values.md#null-values-starting-with-f-9) to make the compiler indicate possible null values and where they need handling. The above code will then start producing warnings:
702
+
703
+
```fsharp
704
+
let checkNonNull argName (arg: obj) =
705
+
match arg with
706
+
// nullness warning: the type 'obj' does not support 'null'
707
+
| null -> nullArg argName
708
+
| _ -> ()
709
+
710
+
let checkNonNull' argName (arg: obj) =
711
+
// nullness warning: the type 'obj' does not support 'null'
712
+
if isNull arg then nullArg argName
713
+
else ()
714
+
```
715
+
716
+
To address the warnings, you need to explicitly specify `null` as a possible argument value:
717
+
718
+
```fsharp
719
+
let checkNonNull argName (arg: obj | null) =
720
+
match arg with
721
+
| null -> nullArg argName
722
+
| _ -> ()
723
+
724
+
let checkNonNull' argName (arg: obj | null) =
725
+
if isNull arg then nullArg argName
726
+
else ()
727
+
```
728
+
729
+
On the other hand, you'll get a warning if the compiler detects that a possible null value is not handled:
730
+
731
+
```fsharp
732
+
let printLineLength (s: string) =
733
+
printfn "%i" s.Length
734
+
735
+
let readLineFromStream (sr: System.IO.StreamReader) =
736
+
let line = sr.ReadLine()
737
+
// nullness warning: The types 'string' and 'string | null'
738
+
// do not have equivalent nullability
739
+
printLineLength line
740
+
```
741
+
742
+
These warnings should be addressed using idiomatic F# [null pattern](../language-reference/pattern-matching.md#null-pattern) via pattern matching:
743
+
```fsharp
744
+
let printLineLength (s: string) =
745
+
printfn "%i" s.Length
746
+
747
+
let readLineFromStream (sr: System.IO.StreamReader) =
748
+
let line = sr.ReadLine()
749
+
match line with
750
+
| null -> ()
751
+
| s -> printLineLength s
752
+
```
753
+
701
754
#### Avoid using tuples as return values
702
755
703
756
Instead, prefer returning a named type holding the aggregate data, or using out parameters to return multiple values. Although tuples and struct tuples exist in .NET (including C# language support for struct tuples), they will most often not provide the ideal and expected API for .NET developers.
Copy file name to clipboardExpand all lines: docs/fsharp/style-guide/conventions.md
+30Lines changed: 30 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -687,6 +687,36 @@ module Array =
687
687
688
688
For legacy reasons some string functions in FSharp.Core still treat nulls as empty strings and do not fail on null arguments. However do not take this as guidance, and do not adopt coding patterns that attribute any semantic meaning to "null".
689
689
690
+
### Leverage F# 9 null syntax at the API boundaries
691
+
692
+
F# 9 adds [syntax](../language-reference/values/null-values.md#null-values-starting-with-f-9) to explicitly state that a value can be null. It's designed to be used on the API boundaries, to make the compiler indicate the places where null handling null is missing.
693
+
694
+
Here is an example of the valid usage of the syntax:
695
+
```fsharp
696
+
let processStream (stream: System.IO.StreamReader) =
697
+
let processLine (line: string | null) =
698
+
match line with
699
+
| null -> (); false
700
+
| s -> printfn "%s" s; true
701
+
702
+
while processLine(stream.ReadLine()) do ()
703
+
stream.Close()
704
+
```
705
+
706
+
**Avoid** propagating nulls further down your F# code:
707
+
```fsharp
708
+
let getLineFromStream (stream: System.IO.StreamReader) : string | null =
709
+
stream.ReadLine()
710
+
```
711
+
712
+
Instead, use idiomatic F# means (e.g., options):
713
+
```fsharp
714
+
let getLineFromStream (stream: System.IO.StreamReader) : string option =
715
+
match stream.ReadLine() with
716
+
| null -> None
717
+
| s -> Some s
718
+
```
719
+
690
720
## Object programming
691
721
692
722
F# has full support for objects and object-oriented (OO) concepts. Although many OO concepts are powerful and useful, not all of them are ideal to use. The following lists offer guidance on categories of OO features at a high level.
0 commit comments