<Query Kind="FSharpProgram" />

//type option<'a> =  // 'a option
//    | Some of 'a
//    | None

//let divby t = 100 / t
//printfn "%A %A" (divby 5) (divby 0)    

let divby t = if t <> 0 then Some (100/t) else None
printfn "%A, %A" (divby 5) (divby 0)    

/// ---

let unit t = Some t

let flat' m2 = 
    if Option.isSome m2 then Option.get m2
    else None
    
let flat m2 =  
    match m2 with 
    | Some m -> m
    | None -> None
    
let map' h m = Option.map h m
let map h m = 
    match m with 
    | Some t -> h t |> unit
    | None -> None

let bind' g m = Option.bind g m
let bind g m = 
    match m with 
    | Some t -> g t
    | None -> None

let flat'' m2 = bind id m2
let map'' h = bind (h >> unit)
let bind'' g = map g >> flat

let h t = t+1
let f t = Some t
let g t = Some (t+1)

printfn "unit: %A => %A" 3 (unit 3)
printfn "flat: %A => %A, %A => %A, %A => %A" (Some (Some 3)) (flat (Some (Some 3))) (Some None) (flat (Some None)) None (flat None)
printfn "map h: %A => %A, %A => %A" (Some 3) (map h (Some 3)) None (map h None)
printfn "map g: %A => %A, %A => %A" (Some 3) (map g (Some 3)) None (map g None)
printfn "bind g: %A => %A, %A => %A" (Some 3) (bind g (Some 3)) None (bind g None)

let (>>=) m g = m |> bind g
let (>=>) f g = f >> bind g

printfn "kleisli: %A => %A, %A => %A" (Some 3) ((Some 3) >>= (f >=> g)) None (None >>= (f >=> g))

type MaybeBuilder () =
    member this.Bind (m, g) = bind g m  // let! do!
    member this.ReturnFrom (m) = m      // return!
    member this.Return (t) = unit t     // return

let mu = MaybeBuilder ()  // new

let x m = mu {
    let! t = m
    let! u = f t
    return! g u
}

printfn "sugar: %A => %A, %A => %A" (Some 3) (x (Some 3)) None (x None)

let y m =
    mu.Bind (m,
        fun t -> 
            mu.Bind (f t,
                fun u -> mu.ReturnFrom (g u)))

printfn "builder: %A => %A, %A => %A" (Some 3) (y (Some 3)) None (y None)
//