Reputation: 824
In writing some code that works with a lot of nested async workflows lately I've found a pattern emerging that smells to me. A simple example:
let flip f x y = f y x
let slowInc x = async {
do! Async.Sleep 500
printfn "Here you go, %d" x
}
let verboseFun inp = async {
match List.tryFind (flip (>) 3) inp with
| Some x -> do! slowInc x
| _ -> ()
}
verboseFun [1..5] |> Async.RunSynchronously
The 'verboseFun' to me seems verbose but I can't think of a way to combine the Option and Async monads so it can be rewritten without the pattern match. I was thinking something like
let terseFun inp = async {
inp
|> List.tryFind (flip (>) 3)
|> Option.iterAsync slowInc
}
It just seemed to me that it's highly likely I just don't know what building blocks are available to achieve this.
EDIT: Extra clarification after Tomas' answer.
I was trying to adapt what would be trivial to me if everything was synchronous, e.g.,
let terseFun inp =
inp
|> List.tryFind (flip (>) 3)
|> Option.iter someSideEffectFunciton
to become part of nested async workflows. Originally I was thinking "just chuck a do! in there" so came up with
let terseFun inp = async {
inp
|> List.tryFind (flip (>) 3)
|> Option.iter (fun x -> async { do! someSideEffectFunciton x })
|> ignore
}
But it immediately smelled wrong to me because VS started demanding the ignore. Hope this helps clarify.
Upvotes: 1
Views: 776
Reputation: 243061
The ExtCore library has a bunch of helper functions that let you work with asynchronous computations that return optional values, i.e. of type Async<'T option>
and it even defines asyncMaybe
computation builder for working with them.
I have not used it extensively, but from a few simple experiments I did, it looks like it is not as nicely integrated with the rest of F#'s async
functionality as it perhaps could be, but if you want to go in this direction, ExtCore is probably the best library around.
The following is using the iter
function from AsyncMaybe.Array
(source is here). It is a bit ugly, because I had to make slowInc
be of type Async<unit option>
, but it is pretty close to what you asked for:
let slowInc x = async {
do! Async.Sleep 500
printfn "Here you go, %d" x
return Some ()
}
let verboseFun inp =
inp
|> List.tryFind (fun x -> 3 > x)
|> Array.ofSeq
|> AsyncMaybe.Array.iter slowInc
|> Async.Ignore
Aside, I also removed your flip
function, because this is not generally recommended style in F# (it tends to make code cryptic).
That said, I think you don't really need an entire ExtCore library. It is hard to see what is your general pattern from just one example you posted, but if all your code snippets look similar to the one you posted, you can just define your own asyncIter
function and then use it elsewhere:
let asyncIter f inp = async {
match inp with
| None -> ()
| Some v -> do! f v }
let verboseFun inp =
inp
|> List.tryFind (fun x -> x > 3)
|> asyncIter slowInc
The great thing about F# is that it is really easy to write these abstractions yourself and make them so that they exactly match your needs :-)
Upvotes: 2