FSharp.Interop.NullOptAble


Real World-ish examples using the Operators

1: 
2: 
3: 
4: 
5: 
module RealWorldOperatorsTests

open System
open System.Text
open FSharp.Interop.NullOptAble.Operators

DefaultWith operator. Handy abbreviation of matching a null/nullable/none case.

1: 
2: 
3: 
4: 
5: 
6: 
7: 
[<Fact>] 
let ``null defaultWith`` () =
    let x = Nullable()
    x |?-> lazy 3 |> should equal 3
let `` defaultWith not called if not needed`` () =
    let x = Nullable(3)
    x |?-> lazy (failwith "doesn't get run") |> should equal 3

Binding operator means function isn't applied if a None parameter. However I suggest using computational expressions over these bind operators in most cases.

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
[<Fact>]          
let ``Basic concat`` () =
    let x = "Hello "
    let y = "World"
    (x,y) ||>? ( + ) |> should equal (Some "Hello World")
[<Fact>]
let ``Basic concat none`` () =
    let x = "Hello "
    let y:string = null
    (x,y) ||>? ( + ) |> should equal (None)

Binding doesn't work if function returns a non-_:null or Nullable<_> or Option<_>. You can use map operator instead |>?@ but becareful don't use it on something that could be null.

1: 
2: 
3: 
4: 
5: 
[<Fact>]
let ``Basic nullable math`` () =
    let x = Nullable(3)
    let y = Nullable(3)
    (x,y) ||>?@ ( + ) |> should equal (Some 6)

Doing the things found in this MSDN Blog Post did with the Safe Nav operator

 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: 
[<Fact>]
let ``Safe Navigation Operator Example`` ()=
    let getChild (n:Node) = n.child
    let parent = Node()
    parent 
        |>? getChild 
        |>? getChild
        |>? getChild
        |> should equal None

[<Fact>] 
let ``Safe Navigation Operator Seq Example`` ()=
    let getChild (n:Node) = n.child
    let parents = [
                    Node() //parent = some
                    Node() |> Node //parent.child = some
                    Node() |> Node |> Node //parent.child.child = some
                    Node() |> Node |> Node |> Node //parent.child.child.child = some
                  ]
    seq {
        for parent in parents  do
            yield parent 
                |>? getChild 
                |>? getChild
                |>? getChild
    } 
        |> Seq.choose id
        |> Seq.length |> should equal 1

Solution using |>? for this RNA Transcription problem from exercism.

Note: |>? fun (s:StringBuilder) -> This is because |>? needs argument for lambda overloads to work.

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
[<Fact>]
let ``RNA transcriptions `` () =
    let toRna (dna: string): string option = 
        let combine (sb:StringBuilder option) =
            let append (c:char) = 
                sb |>? (fun (s:StringBuilder) -> s.Append(c))       
            function
            | 'G' -> 'C' |> append
            | 'C' -> 'G' |> append
            | 'T' -> 'A' |> append
            | 'A' -> 'U' |> append
            | ___ -> None
        dna
           |>? Seq.fold combine (Some <| StringBuilder())
           |>? string

    //test case from exercism
    toRna "ACGTGGTCTTAA" |> should equal (Some "UGCACCAGAAUU")
module RealWorldOperatorsTests
namespace System
namespace System.Text
Multiple items
namespace FSharp

--------------------
namespace Microsoft.FSharp
namespace FSharp.Interop
namespace FSharp.Interop.NullOptAble
module Operators

from FSharp.Interop.NullOptAble
val ( null defaultWith ) : unit -> 'a
val x : obj
val ( defaultWith not called if not needed ) : unit -> 'a
val x : 'a (requires member DefaultWith)
val failwith : message:string -> 'T
val ( Basic concat ) : unit -> 'a
val x : string
val y : string
union case Option.Some: Value: 'T -> Option<'T>
val ( Basic concat none ) : unit -> 'a
Multiple items
val string : value:'T -> string

--------------------
type string = System.String
union case Option.None: Option<'T>
val ( Basic nullable math ) : unit -> 'a
val x : 'a
val y : 'a
Multiple items
type AllowNullLiteralAttribute =
  inherit Attribute
  new : unit -> AllowNullLiteralAttribute
  new : value:bool -> AllowNullLiteralAttribute
  member Value : bool

--------------------
new : unit -> AllowNullLiteralAttribute
new : value:bool -> AllowNullLiteralAttribute
Multiple items
type Node =
  new : unit -> Node
  new : child:Node -> Node
  member child : Node
  member child : Node with set

--------------------
new : unit -> Node
new : child:Node -> Node
val child : Node
val set : elements:seq<'T> -> Set<'T> (requires comparison)
val ( Safe Navigation Operator Example ) : unit -> 'a
val getChild : (Node -> Node)
val n : Node
property Node.child: Node
val parent : Node
val ( Safe Navigation Operator Seq Example ) : unit -> 'a
val parents : Node list
Multiple items
val seq : sequence:seq<'T> -> seq<'T>

--------------------
type seq<'T> = System.Collections.Generic.IEnumerable<'T>
module Seq

from Microsoft.FSharp.Collections
val choose : chooser:('T -> 'U option) -> source:seq<'T> -> seq<'U>
val id : x:'T -> 'T
val length : source:seq<'T> -> int
val ( RNA transcriptions ) : unit -> 'a
val toRna : (string -> string option)
val dna : string
type 'T option = Option<'T>
val combine : (StringBuilder option -> char -> 'a option)
val sb : StringBuilder option
Multiple items
type StringBuilder =
  new : unit -> StringBuilder + 5 overloads
  member Append : value:string -> StringBuilder + 22 overloads
  member AppendFormat : format:string * arg0:obj -> StringBuilder + 7 overloads
  member AppendJoin : separator:string * [<ParamArray>] values:obj[] -> StringBuilder + 5 overloads
  member AppendLine : unit -> StringBuilder + 1 overload
  member Capacity : int with get, set
  member Chars : int -> char with get, set
  member Clear : unit -> StringBuilder
  member CopyTo : sourceIndex:int * destination:Span<char> * count:int -> unit + 1 overload
  member EnsureCapacity : capacity:int -> int
  ...

--------------------
StringBuilder() : StringBuilder
StringBuilder(capacity: int) : StringBuilder
StringBuilder(value: string) : StringBuilder
StringBuilder(value: string, capacity: int) : StringBuilder
StringBuilder(capacity: int, maxCapacity: int) : StringBuilder
StringBuilder(value: string, startIndex: int, length: int, capacity: int) : StringBuilder
val append : (char -> 'a option)
val c : char
Multiple items
val char : value:'T -> char (requires member op_Explicit)

--------------------
type char = System.Char
val s : StringBuilder
StringBuilder.Append(value: System.ReadOnlySpan<char>) : StringBuilder
   (+0 other overloads)
StringBuilder.Append(value: char []) : StringBuilder
   (+0 other overloads)
StringBuilder.Append(value: obj) : StringBuilder
   (+0 other overloads)
StringBuilder.Append(value: uint64) : StringBuilder
   (+0 other overloads)
StringBuilder.Append(value: uint32) : StringBuilder
   (+0 other overloads)
StringBuilder.Append(value: uint16) : StringBuilder
   (+0 other overloads)
StringBuilder.Append(value: decimal) : StringBuilder
   (+0 other overloads)
StringBuilder.Append(value: float) : StringBuilder
   (+0 other overloads)
StringBuilder.Append(value: float32) : StringBuilder
   (+0 other overloads)
StringBuilder.Append(value: int64) : StringBuilder
   (+0 other overloads)
val ___ : char
val fold : folder:('State -> 'T -> 'State) -> state:'State -> source:seq<'T> -> 'State
Fork me on GitHub