Last active
August 21, 2017 22:34
-
-
Save panesofglass/f36ef58b62dfe65d3380183b276b13bc to your computer and use it in GitHub Desktop.
F# async applicative using a custom operation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Nice, but I wonder if defining and!
as 'run both operations in parallel' is the right abstraction for Async.
I mean, Async is async, not necessarily parallel.
What do you think?
@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
Types of
x
andy
in the first part ofcomp
areobj
, though the finalyield x, y
is typed asint * int
. Also, defining alet
binding in that computation causes the type inference to get messed up. Results were definitely run in parallel, though:Compare with
compBind
:Results of running
comp3
with multipleand!
combinators: