2 people like it.

Reimplementation of C# Nullable type

Type-safe Nullable in F# featuring: arithmetic operations on primitive types, structural equality/comparison, null-coalescing operator, explicit type conversion to the underlying type, throwing an exception in exceptional condition, `nullable` computation expression, and etc.

Implementation

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
24: 
25: 
26: 
27: 
28: 
29: 
30: 
31: 
32: 
33: 
34: 
35: 
36: 
37: 
38: 
39: 
40: 
41: 
42: 
43: 
44: 
45: 
46: 
47: 
48: 
49: 
50: 
51: 
52: 
53: 
54: 
55: 
56: 
57: 
58: 
59: 
60: 
61: 
62: 
63: 
64: 
65: 
66: 
67: 
68: 
69: 
70: 
71: 
72: 
73: 
74: 
75: 
76: 
77: 
78: 
79: 
80: 
81: 
82: 
83: 
84: 
85: 
86: 
// XXX Uncomment the following line:
//[<Struct>]
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

[<AutoOpen>]
module FSharpNullableComputationExpression =
    let nullable = FSharpNullableBuilder()

[<AutoOpen>]
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)

Examples

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
24: 
25: 
26: 
27: 
28: 
29: 
30: 
31: 
32: 
33: 
34: 
35: 
36: 
37: 
38: 
39: 
40: 
41: 
// 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<int> = Value 42

// If both sides of the operator are of Nullable, you can omit `.`.
let b = Value 19 + Value 23 // val a : FSharpNullable<int> = 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<int> = Value 84

// `+.` denotes the LHS is of non-nullable and the RHS of FSharpNullable.
let c = 42 +. a // val c : FSharpNullable<int> = Value 84

// Null is such a scary thing so that it turns everything it touches into Null.
let n = a + Null // val n : FSharpNullable<int> = 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<int>' 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.
union case FSharpNullable.Value: 'T -> FSharpNullable<'T>
union case FSharpNullable.Null: FSharpNullable<'T>
val this : FSharpNullable<'T> (requires value type)
member FSharpNullable.HasValue : bool

Full name: Script.FSharpNullable`1.HasValue
static member FSharpNullable.op_Explicit : this:FSharpNullable<'T> -> 'T

Full name: Script.FSharpNullable`1.op_Explicit
val raise : exn:System.Exception -> 'T

Full name: Microsoft.FSharp.Core.Operators.raise
namespace System
Multiple items
type InvalidOperationException =
  inherit SystemException
  new : unit -> InvalidOperationException + 2 overloads

Full name: System.InvalidOperationException

--------------------
System.InvalidOperationException() : unit
System.InvalidOperationException(message: string) : unit
System.InvalidOperationException(message: string, innerException: exn) : unit
val x : 'T (requires value type)
val lhs : FSharpNullable<'a> (requires value type)
val rhs : 'a (requires value type)
val x : 'a (requires value type)
type FSharpNullable<'T (requires value type)> =
  | Value of 'T
  | Null
  member HasValue : bool
  static member ( + ) : lhs:FSharpNullable<'a> * rhs:FSharpNullable<'a> -> FSharpNullable<'a> (requires value type and member ( + ))
  static member ( /. ) : lhs:'a * rhs:FSharpNullable<'a> -> FSharpNullable<'a> (requires member ( / ) and value type)
  static member ( / ) : lhs:FSharpNullable<'a> * rhs:FSharpNullable<'a> -> FSharpNullable<'a> (requires value type and member ( / ))
  static member ( ./ ) : lhs:FSharpNullable<'a> * rhs:'a -> FSharpNullable<'a> (requires member ( / ) and value type)
  static member ( ./. ) : lhs:FSharpNullable<'a> * rhs:FSharpNullable<'a> -> FSharpNullable<'a> (requires value type and member ( / ))
  static member ( .- ) : lhs:FSharpNullable<'a> * rhs:'a -> FSharpNullable<'a> (requires member ( - ) and value type)
  static member ( .-. ) : lhs:FSharpNullable<'a> * rhs:FSharpNullable<'a> -> FSharpNullable<'a> (requires value type and member ( - ))
  static member ( .* ) : lhs:FSharpNullable<'a> * rhs:'a -> FSharpNullable<'a> (requires member ( * ) and value type)
  static member ( .*. ) : lhs:FSharpNullable<'a> * rhs:FSharpNullable<'a> -> FSharpNullable<'a> (requires value type and member ( * ))
  static member ( .+ ) : lhs:FSharpNullable<'a> * rhs:'a -> FSharpNullable<'a> (requires member ( + ) and value type)
  static member ( .+. ) : lhs:FSharpNullable<'a> * rhs:FSharpNullable<'a> -> FSharpNullable<'a> (requires value type and member ( + ))
  static member op_Explicit : this:FSharpNullable<'T> -> 'T
  static member ( <??> ) : lhs:FSharpNullable<'a> * rhs:'a -> 'a (requires value type)
  static member ( -. ) : lhs:'a * rhs:FSharpNullable<'a> -> FSharpNullable<'a> (requires member ( - ) and value type)
  static member ( * ) : lhs:FSharpNullable<'a> * rhs:FSharpNullable<'a> -> FSharpNullable<'a> (requires value type and member ( * ))
  static member ( *. ) : lhs:'a * rhs:FSharpNullable<'a> -> FSharpNullable<'a> (requires member ( * ) and value type)
  static member ( +. ) : lhs:'a * rhs:FSharpNullable<'a> -> FSharpNullable<'a> (requires member ( + ) and value type)
  static member ( - ) : lhs:FSharpNullable<'a> * rhs:FSharpNullable<'a> -> FSharpNullable<'a> (requires value type and member ( - ))

Full name: Script.FSharpNullable<_>
val bind : f:('a -> FSharpNullable<'b>) -> x:FSharpNullable<'a> -> FSharpNullable<'b> (requires value type and value type)

Full name: Script.FSharpNullable.bind
val f : ('a -> FSharpNullable<'b>) (requires value type and value type)
val x : FSharpNullable<'a> (requires value type)
val x' : 'a (requires value type)
Multiple items
type FSharpNullableBuilder =
  new : unit -> FSharpNullableBuilder
  member Bind : x:FSharpNullable<'b> * f:('b -> FSharpNullable<'c>) -> FSharpNullable<'c> (requires value type and value type)
  member Return : x:'a -> FSharpNullable<'a> (requires value type)

Full name: Script.FSharpNullableBuilder

--------------------
new : unit -> FSharpNullableBuilder
member FSharpNullableBuilder.Bind : x:FSharpNullable<'b> * f:('b -> FSharpNullable<'c>) -> FSharpNullable<'c> (requires value type and value type)

Full name: Script.FSharpNullableBuilder.Bind
val x : FSharpNullable<'b> (requires value type)
val f : ('b -> FSharpNullable<'c>) (requires value type and value type)
Multiple items
module FSharpNullable

from Script

--------------------
type FSharpNullable<'T (requires value type)> =
  | Value of 'T
  | Null
  member HasValue : bool
  static member ( + ) : lhs:FSharpNullable<'a> * rhs:FSharpNullable<'a> -> FSharpNullable<'a> (requires value type and member ( + ))
  static member ( /. ) : lhs:'a * rhs:FSharpNullable<'a> -> FSharpNullable<'a> (requires member ( / ) and value type)
  static member ( / ) : lhs:FSharpNullable<'a> * rhs:FSharpNullable<'a> -> FSharpNullable<'a> (requires value type and member ( / ))
  static member ( ./ ) : lhs:FSharpNullable<'a> * rhs:'a -> FSharpNullable<'a> (requires member ( / ) and value type)
  static member ( ./. ) : lhs:FSharpNullable<'a> * rhs:FSharpNullable<'a> -> FSharpNullable<'a> (requires value type and member ( / ))
  static member ( .- ) : lhs:FSharpNullable<'a> * rhs:'a -> FSharpNullable<'a> (requires member ( - ) and value type)
  static member ( .-. ) : lhs:FSharpNullable<'a> * rhs:FSharpNullable<'a> -> FSharpNullable<'a> (requires value type and member ( - ))
  static member ( .* ) : lhs:FSharpNullable<'a> * rhs:'a -> FSharpNullable<'a> (requires member ( * ) and value type)
  static member ( .*. ) : lhs:FSharpNullable<'a> * rhs:FSharpNullable<'a> -> FSharpNullable<'a> (requires value type and member ( * ))
  static member ( .+ ) : lhs:FSharpNullable<'a> * rhs:'a -> FSharpNullable<'a> (requires member ( + ) and value type)
  static member ( .+. ) : lhs:FSharpNullable<'a> * rhs:FSharpNullable<'a> -> FSharpNullable<'a> (requires value type and member ( + ))
  static member op_Explicit : this:FSharpNullable<'T> -> 'T
  static member ( <??> ) : lhs:FSharpNullable<'a> * rhs:'a -> 'a (requires value type)
  static member ( -. ) : lhs:'a * rhs:FSharpNullable<'a> -> FSharpNullable<'a> (requires member ( - ) and value type)
  static member ( * ) : lhs:FSharpNullable<'a> * rhs:FSharpNullable<'a> -> FSharpNullable<'a> (requires value type and member ( * ))
  static member ( *. ) : lhs:'a * rhs:FSharpNullable<'a> -> FSharpNullable<'a> (requires member ( * ) and value type)
  static member ( +. ) : lhs:'a * rhs:FSharpNullable<'a> -> FSharpNullable<'a> (requires member ( + ) and value type)
  static member ( - ) : lhs:FSharpNullable<'a> * rhs:FSharpNullable<'a> -> FSharpNullable<'a> (requires value type and member ( - ))

Full name: Script.FSharpNullable<_>
val __ : FSharpNullableBuilder
member FSharpNullableBuilder.Return : x:'a -> FSharpNullable<'a> (requires value type)

Full name: Script.FSharpNullableBuilder.Return
Multiple items
type AutoOpenAttribute =
  inherit Attribute
  new : unit -> AutoOpenAttribute
  new : path:string -> AutoOpenAttribute
  member Path : string

Full name: Microsoft.FSharp.Core.AutoOpenAttribute

--------------------
new : unit -> AutoOpenAttribute
new : path:string -> AutoOpenAttribute
val nullable : FSharpNullableBuilder

Full name: Script.FSharpNullableComputationExpression.nullable
val private add : a:FSharpNullable<'T> -> b:FSharpNullable<'T> -> FSharpNullable<'T> (requires value type and member ( + ))

Full name: Script.FSharpNullableOps.add
val a : FSharpNullable<'T> (requires value type and member ( + ))
val b : FSharpNullable<'T> (requires value type and member ( + ))
val x : 'T (requires value type and member ( + ))
val y : 'T (requires value type and member ( + ))
val private subtract : a:FSharpNullable<'T> -> b:FSharpNullable<'T> -> FSharpNullable<'T> (requires value type and member ( - ))

Full name: Script.FSharpNullableOps.subtract
val a : FSharpNullable<'T> (requires value type and member ( - ))
val b : FSharpNullable<'T> (requires value type and member ( - ))
val x : 'T (requires value type and member ( - ))
val y : 'T (requires value type and member ( - ))
val private multiply : a:FSharpNullable<'T> -> b:FSharpNullable<'T> -> FSharpNullable<'T> (requires value type and member ( * ))

Full name: Script.FSharpNullableOps.multiply
val a : FSharpNullable<'T> (requires value type and member ( * ))
val b : FSharpNullable<'T> (requires value type and member ( * ))
val x : 'T (requires value type and member ( * ))
val y : 'T (requires value type and member ( * ))
val private divide : a:FSharpNullable<'T> -> b:FSharpNullable<'T> -> FSharpNullable<'T> (requires value type and member ( / ))

Full name: Script.FSharpNullableOps.divide
val a : FSharpNullable<'T> (requires value type and member ( / ))
val b : FSharpNullable<'T> (requires value type and member ( / ))
val x : 'T (requires value type and member ( / ))
val y : 'T (requires value type and member ( / ))
val lhs : FSharpNullable<'a> (requires value type and member ( + ))
val rhs : FSharpNullable<'a> (requires value type and member ( + ))
val lhs : 'a (requires member ( + ) and value type)
val rhs : FSharpNullable<'a> (requires member ( + ) and value type)
val lhs : FSharpNullable<'a> (requires member ( + ) and value type)
val rhs : 'a (requires member ( + ) and value type)
val lhs : FSharpNullable<'a> (requires value type and member ( - ))
val rhs : FSharpNullable<'a> (requires value type and member ( - ))
val lhs : 'a (requires member ( - ) and value type)
val rhs : FSharpNullable<'a> (requires member ( - ) and value type)
val lhs : FSharpNullable<'a> (requires member ( - ) and value type)
val rhs : 'a (requires member ( - ) and value type)
val lhs : FSharpNullable<'a> (requires value type and member ( * ))
val rhs : FSharpNullable<'a> (requires value type and member ( * ))
val lhs : 'a (requires member ( * ) and value type)
val rhs : FSharpNullable<'a> (requires member ( * ) and value type)
val lhs : FSharpNullable<'a> (requires member ( * ) and value type)
val rhs : 'a (requires member ( * ) and value type)
val lhs : FSharpNullable<'a> (requires value type and member ( / ))
val rhs : FSharpNullable<'a> (requires value type and member ( / ))
val lhs : 'a (requires member ( / ) and value type)
val rhs : FSharpNullable<'a> (requires member ( / ) and value type)
val lhs : FSharpNullable<'a> (requires member ( / ) and value type)
val rhs : 'a (requires member ( / ) and value type)
val a : FSharpNullable<int>

Full name: Script.a
val b : FSharpNullable<int>

Full name: Script.b
val c : FSharpNullable<int>

Full name: Script.c
val n : FSharpNullable<int>

Full name: Script.n
property FSharpNullable.HasValue: bool
Multiple items
val int : value:'T -> int (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.int

--------------------
type int = int32

Full name: Microsoft.FSharp.Core.int

--------------------
type int<'Measure> = int

Full name: Microsoft.FSharp.Core.int<_>
Multiple items
val int64 : value:'T -> int64 (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.int64

--------------------
type int64 = System.Int64

Full name: Microsoft.FSharp.Core.int64

--------------------
type int64<'Measure> = int64

Full name: Microsoft.FSharp.Core.int64<_>

More information

Link:http://fssnip.net/7WZ
Posted:4 years ago
Author:Bang Jun-young
Tags: nullable