FSharp.Interop.NullOptAble


Real World-ish examples

1: 
2: 
3: 
4: 
module RealWorldTests
open System
open System.Text
open FSharp.Interop.NullOptAble

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: 
[<Fact>]
let ``Safe Navigation Operator Example`` ()=
    
    let parent = Node()
    option {
        let! a = parent.child
        let! b = a.child
        let! c = b.child
        return c
    } |> should equal None
    
[<Fact>] 
let ``Safe Navigation Operator Seq Example`` ()=
    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
                  ]
    chooseSeq {
        for parent in parents  do
            let! a = parent.child
            let! b = a.child
            let! c = b.child
            yield c
    } |> Seq.length |> should equal 1

Guard is just an easy wait for nulloptable binding while dealing with mutations and external state.

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
[<Fact>]
let ``Guard Example, for dealing with non functional statey things`` () =
    let overrideSetup : Setup option  = None
    let randoFilename = "2713157a-4333-462e-b9e1-c03e0ca6d5af.txt"
    let existsActual = System.IO.File.Exists(randoFilename)
    guard{
        let! setup = overrideSetup
        if setup.ShouldWriteFile then
            use f = System.IO.File.Open(randoFilename, IO.FileMode.CreateNew)  //Never gets run
            ()
    }
    System.IO.File.Exists(randoFilename) |> should equal existsActual

Modified this recursive prime fucntion from F# docs example.

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
[<Fact>] 
let ``IsPrime Example`` ()=
    let isprime n =
        let rec check i =
            n <> 1 && (i > n/2 || (n % i <> 0 && check (i + 1)))
        Option.ofTryTuple (check 2, n)

    let prime = chooseSeq { for n in 1..100 do 
                                let! p = isprime n
                                yield p }

    //verified against list
    prime |> Seq.toList
          |> should equal [2; 3; 5; 7; 11; 13; 17; 19; 23; 29; 31; 37; 
                            41; 43; 47; 53; 59; 61; 67; 71; 73; 79; 83; 89; 97]

Solution using option {} for this RNA Transcription problem from exercism.

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
[<Fact>]
let ``RNA transcriptions `` () =
    let toRna (dna: string): string option = 
        let combine (sb:StringBuilder option) =
            let append (c:char) = option {
                    let! sb' = sb
                    return sb'.Append(c)
                }
            function
            | 'G' -> 'C' |> append
            | 'C' -> 'G' |> append
            | 'T' -> 'A' |> append
            | 'A' -> 'U' |> append
            | ___ -> None
        option {
            let! dna' = dna //handles if string is null
            let! sb' = dna' |> Seq.fold combine (Some <| StringBuilder())
            return sb'.ToString()
        }
        
    //test case from exercism
    toRna "ACGTGGTCTTAA" |> should equal (Some "UGCACCAGAAUU")

Solution using chooseSeq {} for this Rain Drops (Fizz buzz) problem from exercism.

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
[<Fact>]
let ``rain drops`` () =
    let convert (number: int): string =
        let drop x s = Option.ofTryTuple (number % x = 0, s)
        chooseSeq {
            yield! drop 3 "Pling"
            yield! drop 5 "Plang"
            yield! drop 7 "Plong"
        } |> String.concat ""
          |> function | "" -> string(number)
                      | s -> s

    //test cases from exercism
    convert 49 |> should equal "Plong"
    convert 105 |> should equal "PlingPlangPlong"
    convert 52 |> should equal "52"

Example using option {} for All Your Base problem from exercism.

 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: 
[<Fact>]
let ``All your Base`` () = 

    let rebase digits inputBase outputBase = 
        let fromBase b digits' =
            let foldToBase10 (acc:int option) d = option {
                if d >= 0 && d < b then
                    let! acc' = acc
                    return (acc' * b + d)
            }
            digits' |> List.fold foldToBase10 (Some 0)
        let toBase b n = option {
            return Seq.initInfinite ignore
                   |> Seq.scan (fun (n', _) _ ->  n' / b, n' % b) (n, 0)
                   |> Seq.pairwise
                   |> Seq.takeWhile (fst >> (fun  (n', _) -> n' > 0))
                   |> Seq.map snd
                   |> Seq.fold (fun acc (_, d) -> d::acc) [] 
                   |> function [] -> [0] | x -> x
        }
        option {
            if inputBase >= 2 && outputBase >= 2 then
                let! base10 =
                    digits
                    |> List.skipWhile ((=) 0) //strip prefixes
                    |> fromBase inputBase
                return! base10 |> toBase outputBase
        }
    
    rebase [1; 1; 2; 0] 3 16 |> should equal (Some [2; 10])
    rebase [1; 2; 1; 0; 1; 0] 2 10 |> should equal None
module RealWorldTests
namespace System
namespace System.Text
Multiple items
namespace FSharp

--------------------
namespace Microsoft.FSharp
namespace FSharp.Interop
namespace FSharp.Interop.NullOptAble
val ( Basic nullable math ) : unit -> 'a
val x : obj
val y : obj
Multiple items
val option : OptionBuilder

--------------------
type 'T option = Option<'T>
union case Option.Some: Value: 'T -> Option<'T>
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 parent : Node
property Node.child: Node
union case Option.None: Option<'T>
val ( Safe Navigation Operator Seq Example ) : unit -> 'a
val parents : Node list
val chooseSeq : ChooseSeqBuilder
module Seq

from Microsoft.FSharp.Collections
val length : source:seq<'T> -> int
type Setup =
  {ShouldWriteFile: bool;}
Setup.ShouldWriteFile: bool
type bool = System.Boolean
val ( Guard Example, for dealing with non functional statey things ) : unit -> 'a
val overrideSetup : Setup option
val randoFilename : string
val existsActual : bool
namespace System.IO
type File =
  static member AppendAllLines : path:string * contents:IEnumerable<string> -> unit + 1 overload
  static member AppendAllLinesAsync : path:string * contents:IEnumerable<string> * ?cancellationToken:CancellationToken -> Task + 1 overload
  static member AppendAllText : path:string * contents:string -> unit + 1 overload
  static member AppendAllTextAsync : path:string * contents:string * ?cancellationToken:CancellationToken -> Task + 1 overload
  static member AppendText : path:string -> StreamWriter
  static member Copy : sourceFileName:string * destFileName:string -> unit + 1 overload
  static member Create : path:string -> FileStream + 3 overloads
  static member CreateText : path:string -> StreamWriter
  static member Decrypt : path:string -> unit
  static member Delete : path:string -> unit
  ...
System.IO.File.Exists(path: string) : bool
val guard : GuardBuilder
System.IO.File.Open(path: string, mode: System.IO.FileMode) : System.IO.FileStream
System.IO.File.Open(path: string, mode: System.IO.FileMode, access: System.IO.FileAccess) : System.IO.FileStream
System.IO.File.Open(path: string, mode: System.IO.FileMode, access: System.IO.FileAccess, share: System.IO.FileShare) : System.IO.FileStream
val ( IsPrime Example ) : unit -> 'a
val isprime : (int -> int option)
val n : int
val check : (int -> bool)
val i : int
Multiple items
module Option

from FSharp.Interop.NullOptAble

--------------------
module Option

from Microsoft.FSharp.Core
val ofTryTuple : value:(bool * 'a) -> 'a option
val prime : seq<obj>
val toList : source:seq<'T> -> 'T list
val ( RNA transcriptions ) : unit -> 'a
val toRna : (string -> string option)
val dna : string
Multiple items
val string : value:'T -> string

--------------------
type string = System.String
val combine : (StringBuilder option -> char -> 'b 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 -> 'c)
val c : char
Multiple items
val char : value:'T -> char (requires member op_Explicit)

--------------------
type char = System.Char
val ___ : char
val fold : folder:('State -> 'T -> 'State) -> state:'State -> source:seq<'T> -> 'State
val ( rain drops ) : unit -> 'a
val convert : (int -> string)
val number : int
Multiple items
val int : value:'T -> int (requires member op_Explicit)

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

--------------------
type int<'Measure> = int
val drop : (int -> 'b -> 'b option)
val x : int
val s : 'b
module String

from Microsoft.FSharp.Core
val concat : sep:string -> strings:seq<string> -> string
val s : string
val ( All your Base ) : unit -> 'a
val rebase : ('b -> 'c -> 'd -> 'e)
val digits : 'b
val inputBase : 'c
val outputBase : 'd
val fromBase : ('f -> 'g list -> int option)
val b : 'f
val digits' : 'g list
val foldToBase10 : (int option -> 'h -> 'i)
val acc : int option
val d : 'h
Multiple items
module List

from Microsoft.FSharp.Collections

--------------------
type List<'T> =
  | ( [] )
  | ( :: ) of Head: 'T * Tail: 'T list
    interface IReadOnlyList<'T>
    interface IReadOnlyCollection<'T>
    interface IEnumerable
    interface IEnumerable<'T>
    member GetSlice : startIndex:int option * endIndex:int option -> 'T list
    member Head : 'T
    member IsEmpty : bool
    member Item : index:int -> 'T with get
    member Length : int
    member Tail : 'T list
    ...
val fold : folder:('State -> 'T -> 'State) -> state:'State -> list:'T list -> 'State
val toBase : ('f -> 'g -> 'h)
val n : 'g
val initInfinite : initializer:(int -> 'T) -> seq<'T>
val ignore : value:'T -> unit
val scan : folder:('State -> 'T -> 'State) -> state:'State -> source:seq<'T> -> seq<'State>
val pairwise : source:seq<'T> -> seq<'T * 'T>
val takeWhile : predicate:('T -> bool) -> source:seq<'T> -> seq<'T>
val fst : tuple:('T1 * 'T2) -> 'T1
val map : mapping:('T -> 'U) -> source:seq<'T> -> seq<'U>
val snd : tuple:('T1 * 'T2) -> 'T2
val skipWhile : predicate:('T -> bool) -> list:'T list -> 'T list
Fork me on GitHub