Skip to content

Instantly share code, notes, and snippets.

@panesofglass
Last active August 21, 2017 22:34
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save panesofglass/f36ef58b62dfe65d3380183b276b13bc to your computer and use it in GitHub Desktop.
Save panesofglass/f36ef58b62dfe65d3380183b276b13bc to your computer and use it in GitHub Desktop.
F# async applicative using a custom operation
open System
type FSharp.Control.AsyncBuilder with
[<CustomOperation("and!", IsLikeZip=true)>]
member __.Merge(x, y, f) =
async {
let! token = Async.CancellationToken
let! x' = Async.StartChildAsTask x
let! y' = Async.StartChildAsTask y
do System.Threading.Tasks.Task.WaitAll([|x';y'|], cancellationToken = token)
let! x'' = Async.AwaitTask x'
let! y'' = Async.AwaitTask y'
return f x'' y''
}
member __.For(m, f) = __.Bind(m, f)
let a (sw:Diagnostics.Stopwatch) = async {
printfn "starting a %O" sw.ElapsedMilliseconds
do! Async.Sleep 1000
printfn "returning a %O" sw.ElapsedMilliseconds
return 1
}
let b (sw:Diagnostics.Stopwatch) = async {
printfn "starting b %O" sw.ElapsedMilliseconds
do! Async.Sleep 500
do printfn "returning b %O" sw.ElapsedMilliseconds
return 2
}
let comp sw = async {
for x in a sw do
``and!`` y in b sw
return x + y
}
let compBind sw = async {
let! x = a sw
let! y = b sw
return x + y
}
let sw = Diagnostics.Stopwatch.StartNew()
let result = comp sw |> Async.RunSynchronously
sw.Stop()
printfn "comp ran in %Oms with result %i" sw.ElapsedMilliseconds result
// Compare with
sw.Reset()
sw.Start()
let resultBind = compBind sw |> Async.RunSynchronously
sw.Stop()
printfn "compBind ran in %Oms with result %i" sw.ElapsedMilliseconds resultBind
let c (sw:Diagnostics.Stopwatch) = async {
printfn "starting c %O" sw.ElapsedMilliseconds
do! Async.Sleep 1500
do printfn "returning c %O" sw.ElapsedMilliseconds
return 3
}
let comp3 sw = async {
for x in a sw do
``and!`` y in b sw
``and!`` z in c sw
return x + y + z
}
sw.Reset()
sw.Start()
let result3 = comp3 sw |> Async.RunSynchronously
sw.Stop()
printfn "comp3 ran in %Oms with result %i" sw.ElapsedMilliseconds result3
@panesofglass
Copy link
Author

panesofglass commented Aug 21, 2017

@gusty, the general idea is, I think, something like what is available in FSharpx.Async. See this thread. There's no reason it must be parallel, but there's no reason it shouldn't be parallel either. It's beneficial that it is parallel for performance reasons.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment