// [snippet:Implementation]
// XXX Uncomment the following line:
//[]
type FSharpNullable<'T when 'T : struct> =
| Value of 'T
| Null
member this.HasValue =
match this with
| Null -> false
| Value _ -> true
static member inline op_Explicit(this) : 'T =
match this with
| Null -> raise <| System.InvalidOperationException(
"Nullable object must have a value.")
| Value x -> x
static member inline () (lhs, rhs) =
match lhs with
| Null -> rhs
| Value x -> x
module FSharpNullable =
let bind f x =
match x with
| Null -> Null
| Value x' -> f x'
type FSharpNullableBuilder() =
member __.Bind(x, f) = x |> FSharpNullable.bind f
member __.Return(x) = Value x
[]
module FSharpNullableComputationExpression =
let nullable = FSharpNullableBuilder()
[]
module private FSharpNullableOps =
let inline add (a: FSharpNullable<'T>) (b: FSharpNullable<'T>) : FSharpNullable<'T> =
nullable {
let! x = a
let! y = b
return x + y
}
let inline subtract (a: FSharpNullable<'T>) (b: FSharpNullable<'T>) : FSharpNullable<'T> =
nullable {
let! x = a
let! y = b
return x - y
}
let inline multiply (a: FSharpNullable<'T>) (b: FSharpNullable<'T>) : FSharpNullable<'T> =
nullable {
let! x = a
let! y = b
return x * y
}
let inline divide (a: FSharpNullable<'T>) (b: FSharpNullable<'T>) : FSharpNullable<'T> =
nullable {
let! x = a
let! y = b
return x / y
}
type FSharpNullable<'T when 'T : struct> with
static member inline ( + ) (lhs, rhs) = add lhs rhs
static member inline (.+.) (lhs, rhs) = add lhs rhs
static member inline ( +.) (lhs, rhs) = add (Value lhs) rhs
static member inline (.+ ) (lhs, rhs) = add lhs (Value rhs)
static member inline ( - ) (lhs, rhs) = subtract lhs rhs
static member inline (.-.) (lhs, rhs) = subtract lhs rhs
static member inline ( -.) (lhs, rhs) = subtract (Value lhs) rhs
static member inline (.- ) (lhs, rhs) = subtract lhs (Value rhs)
static member inline ( * ) (lhs, rhs) = multiply lhs rhs
static member inline (.*.) (lhs, rhs) = multiply lhs rhs
static member inline ( *.) (lhs, rhs) = multiply (Value lhs) rhs
static member inline (.* ) (lhs, rhs) = multiply lhs (Value rhs)
static member inline ( / ) (lhs, rhs) = divide lhs rhs
static member inline (./.) (lhs, rhs) = divide lhs rhs
static member inline ( /.) (lhs, rhs) = divide (Value lhs) rhs
static member inline (./ ) (lhs, rhs) = divide lhs (Value rhs)
// [/snippet]
// [snippet:Examples]
// Prepend/append `.` to the operator to let the compiler know that the left/right hand side of operand is of FSharpNullable.
let a = Value 13 .+. Value 29 // val b : FSharpNullable = Value 42
// If both sides of the operator are of Nullable, you can omit `.`.
let b = Value 19 + Value 23 // val a : FSharpNullable = Value 42
// Supports structural equality.
a = b // val it : bool = true
// Supports structural comparison.
Value 2 < Value 3 // val it : bool = true
// `.+` denotes the LHS is of FSharpNullable and the RHS non-nullable.
let c = a .+ 42 // val c : FSharpNullable = Value 84
// `+.` denotes the LHS is of non-nullable and the RHS of FSharpNullable.
let c = 42 +. a // val c : FSharpNullable = Value 84
// Null is such a scary thing so that it turns everything it touches into Null.
let n = a + Null // val n : FSharpNullable = Null
// Null-coalescing operator evaluates to the LHS if the LHS is not Null..
a + c 42 // val it : int = 126
// Null-coalescing operator evaluates to the RHS if the LHS is Null.
a + c + n 42 // val it : int = 42
// Supports C# Nullable-like property.
a.HasValue // val it : bool = true
n.HasValue // val it : bool = false
// Explicit type conversion to the underlying type.
int a // val it : int = 42
// Type-safety.
int64 a // error FS0001: The type 'FSharpNullable' does not support a conversion to the type 'int64'
int64 (Value 42L) // val it : int64 = 42L
// Can't type-convert Null in any case.
int n // System.InvalidOperationException: Nullable object must have a value.
// [/snippet]