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: 
 87: 
 88: 
 89: 
 90: 
 91: 
 92: 
 93: 
 94: 
 95: 
 96: 
 97: 
 98: 
 99: 
100: 
101: 
102: 
103: 
104: 
105: 
106: 
107: 
108: 
109: 
110: 
111: 
112: 
113: 
114: 
115: 
116: 
117: 
118: 
119: 
120: 
// XXX Uncomment the following two lines:
//[<StructuralEquality; StructuralComparison>]
//[<Struct>]
type FSharpNullable<'T when 'T : struct> =
    | Value of 'T
    | Null

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 FSharpNullableOps =
    let nullable = FSharpNullableBuilder()

    let inline ( .+. ) (a: FSharpNullable<'T>) (b: FSharpNullable<'T>) : FSharpNullable<'T> =
        nullable {
            let! x = a
            let! y = b
            return x + y
        }

    let inline ( .+ ) (a: FSharpNullable<'T>) (b: 'T) : FSharpNullable<'T> =
        nullable {
            let! x = a
            return x + b
        }

    let inline ( +. ) (a: 'T) (b: FSharpNullable<'T>) : FSharpNullable<'T> =
        nullable {
            let! y = b
            return a + y
        }

    let inline ( .-. ) (a: FSharpNullable<'T>) (b: FSharpNullable<'T>) : FSharpNullable<'T> =
        nullable {
            let! x = a
            let! y = b
            return x - y
        }

    let inline ( .- ) (a: FSharpNullable<'T>) (b: 'T) : FSharpNullable<'T> =
        nullable {
            let! x = a
            return x - b
        }

    let inline ( -. ) (a: 'T) (b: FSharpNullable<'T>) : FSharpNullable<'T> =
        nullable {
            let! y = b
            return a - y
        }

    let inline ( .*. ) (a: FSharpNullable<'T>) (b: FSharpNullable<'T>) : FSharpNullable<'T> =
        nullable {
            let! x = a
            let! y = b
            return x * y
        }

    let inline ( .* ) (a: FSharpNullable<'T>) (b: 'T) : FSharpNullable<'T> =
        nullable {
            let! x = a
            return x * b
        }

    let inline ( *. ) (a: 'T) (b: FSharpNullable<'T>) : FSharpNullable<'T> =
        nullable {
            let! y = b
            return a * y
        }

    let inline ( ./. ) (a: FSharpNullable<'T>) (b: FSharpNullable<'T>) : FSharpNullable<'T> =
        nullable {
            let! x = a
            let! y = b
            return x / y
        }

    let inline ( ./ ) (a: FSharpNullable<'T>) (b: 'T) : FSharpNullable<'T> =
        nullable {
            let! x = a
            return x / b
        }

    let inline ( /. ) (a: 'T) (b: FSharpNullable<'T>) : FSharpNullable<'T> =
        nullable {
            let! y = b
            return a / y
        }

type FSharpNullable<'T when 'T : struct> with
    member this.Bind(f) = this |> FSharpNullable.bind f

    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

    static member inline (+) (a, b) = a .+. b
    static member inline (-) (a, b) = a .-. b
    static member inline (*) (a, b) = a .*. b
    static member inline (/) (a, b) = a ./. b

Examples

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
let a = Value 19 + Value 23 // val a : FSharpNullable<int> = Value 42
let b = Value 13 .+. Value 29 // val b : FSharpNullable<int> = Value 42
a = b // val it : bool = true
let c = a .+ 42 // val c : FSharpNullable<int> = Value 84
let n = a + Null // val n : FSharpNullable<int> = Null

a + c <??> 42 // val it : int = 126
a + c + n <??> 42 // val it : int = 42

int a // val it : int = 42
int64 a // error FS0001: The type 'FSharpNullable<int>' does not support a conversion to the type 'int64'
int64 (Value 42L) // val it : int64 = 42L
int n // System.InvalidOperationException: Nullable object must have a value.
union case FSharpNullable.Value: 'T -> FSharpNullable<'T>
union case FSharpNullable.Null: FSharpNullable<'T>
type FSharpNullable<'T (requires value type)> =
  | Value of 'T
  | Null
  member Bind : f:('T -> FSharpNullable<'a>) -> FSharpNullable<'a> (requires value type)
  member HasValue : bool
  static member ( + ) : a:FSharpNullable<'a> * b:FSharpNullable<'a> -> FSharpNullable<'a> (requires value type and member ( + ))
  static member ( / ) : a:FSharpNullable<'a> * b: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 ( * ) : a:FSharpNullable<'a> * b:FSharpNullable<'a> -> FSharpNullable<'a> (requires value type and member ( * ))
  static member ( - ) : a:FSharpNullable<'a> * b: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 Bind : f:('T -> FSharpNullable<'a>) -> FSharpNullable<'a> (requires value type)
  member HasValue : bool
  static member ( + ) : a:FSharpNullable<'a> * b:FSharpNullable<'a> -> FSharpNullable<'a> (requires value type and member ( + ))
  static member ( / ) : a:FSharpNullable<'a> * b: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 ( * ) : a:FSharpNullable<'a> * b:FSharpNullable<'a> -> FSharpNullable<'a> (requires value type and member ( * ))
  static member ( - ) : a:FSharpNullable<'a> * b: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
val x : 'a (requires value type)
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.FSharpNullableOps.nullable
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 b : 'T (requires value type and member ( + ))
val a : 'T (requires value type and member ( + ))
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 b : 'T (requires value type and member ( - ))
val a : 'T (requires value type and member ( - ))
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 b : 'T (requires value type and member ( * ))
val a : 'T (requires value type and member ( * ))
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 b : 'T (requires value type and member ( / ))
val a : 'T (requires value type and member ( / ))
val this : FSharpNullable<'T> (requires value type)
member FSharpNullable.Bind : f:('T -> FSharpNullable<'a>) -> FSharpNullable<'a> (requires value type)

Full name: Script.FSharpNullable`1.Bind
val f : ('T -> FSharpNullable<'a>) (requires value type and 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 a : FSharpNullable<'a> (requires value type and member ( + ))
val b : FSharpNullable<'a> (requires value type and member ( + ))
val a : FSharpNullable<'a> (requires value type and member ( - ))
val b : FSharpNullable<'a> (requires value type and member ( - ))
val a : FSharpNullable<'a> (requires value type and member ( * ))
val b : FSharpNullable<'a> (requires value type and member ( * ))
val a : FSharpNullable<'a> (requires value type and member ( / ))
val b : FSharpNullable<'a> (requires value type and member ( / ))
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
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