<Query Kind="FSharpProgram" />

open System.Threading
open System.Threading.Tasks

// https://msdn.microsoft.com/en-us/library/ee340326.aspx

// unit : 'T -> Task<'T>
let inline unit t = Task.FromResult t

// bind : ('T -> Task<'U>) -> (Task<'T> -> Task<'U>)
let inline bind g (m:Task<'T>) = 
    let t = m.Result  // blocking call :(
    g t

type MyTaskBuilder () =
  member this.Bind (m, g) = 
    bind g m
  member this.Return t = 
    unit t
  member this.ReturnFrom m = 
    m

let mytask = MyTaskBuilder ()

// a : int -> Task<int>
let a t = mytask {
    Thread.Sleep 100
    return t+1
    }

// a2 : int -> Task<int>
let a2 t = mytask {
    Thread.Sleep 100
    return! a t
    }

// b : int -> Task<int>
let b t = mytask {
    Thread.Sleep 100
    return t+t
    }

// c : int -> Task<int>
let c t = mytask {
    let! u = a t
    return! b u
    }

// d : int -> Task<int>
let d t = mytask {
    let! u = a t
    let! v = b u
    return v
    }

//let d t = 
//    mytask.Bind (a t, 
//        fun u -> 
//            mytask.Bind (b u,
//                fun v -> mytask.Return v))

//let d t = 
//    bind 
//       (fun u -> 
//            bind (fun v -> unit v) (b u))
//       (a t)

let inline unit1 (m:Task<'T>) = m.Result

let a' = unit1 (a 3)
let a2' = unit1 (a2 3)
let b' = unit1 (b 3)
let c' = unit1 (c 3)
let d' = unit1 (d 3)

printfn "a':%d, a2':%d, b':%d, c':%d, d':%d" a' a2' b' c' d'

// ---
printfn ""

// (>>=) : Task<'T> -> ('T -> Task<'U>) -> Task<'U>
let inline (>>=) m f = m |> (bind f) // bind f m

let e' = unit1 (a 3 >>= b)
printfn "e':%d" e'

// (>=>) : ('T -> Task<'U>) -> ('U -> Task<'V>) -> ('T -> Task<'V>)
let inline (>=>) f g = f >> (bind g)

let h = a >=> b

let h' = unit1 (h 3)
printfn "h':%d" h'

// ---
printfn ""

// flat: Task<Task<'T>> -> Task<'T>
let inline flat m = bind id m // unit1

// x : 'T -> Task<Task<'T>>
let x t = unit (unit t) // async { return async { return t }}

// y : 'T -> Task<'T>
let y t = flat (x t)

let y' = unit1 (y 7)
printfn "y':%d" y'

// ---

// map: ('T -> 'U) -> (Task<'T> -> Task<'U>)
let inline map f = bind (f >> unit)

// v : Task<int> -> Task<int>
let v = map (fun t -> t+1)

let v' = unit1 (v (unit 7))
printfn "v':%d" v'

// ---
printfn ""

// bind' : ('T -> Task<'U>) -> (Task<'T> -> Task<'U>)
let inline bind' f = f |> map >> flat

// w : Task<int> -> Task<int>
let w = bind' (fun t -> unit (t+1))

let w' = unit1 (w (unit 7))
printfn "w':%d" w'

// ---
printfn ""

// id' : 'T -> 'T
let inline id' t = t |> (unit >> unit1)

let i' = id' 9 
printfn "i':%d" i'

//