Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

F# async and anonymous functions

I have a method that does some IO with this signature:

member this.IsRestaurantInCategoryAsync(restaurantName: string, restaurantAddress: string, restaurantCategory: string) =
    async { ///long running methods }

I want to invoke it in an anonymous function like so:

this.GetRestaurants()
        |> Seq.filter(fun (name, address) -> categoryRepository.IsRestaurantInCategoryAsync(name, address, category))
        |> Seq.toList

The problem is that the IsRestaurantInCategoryAsync returns a async<bool>, not a bool. How do I have the Seq.Filter handle it?

Should I cast the async<bool> to a bool using a let! so then I have to write a non-anonymous function to assign the return?

like image 216
Jamie Dixon Avatar asked Feb 24 '26 02:02

Jamie Dixon


1 Answers

You could use Async.RunSynchronously to run the operation synchronously - but that would defeat the point of using asynchronous workflows to avoid writing blocking code, so that is not the right way to go!

There are different ways to do it - you could iterate over all restaurants sequentially (which will handle one by one) or you could run the filtering in parallel (which will use as many threadpool threads as .NET finds good).

The parallel version looks like this:

let checkAll = async {
  let! all =  
    [ for r in this.GetRestaurants() -> async {
        let! include = catagoryRepository.IsRestaurantInCatagoryAsync(name, address,catagory) 
        if include then return Some(r) else return None } ]
    |> Async.Parallel
  let included = Seq.choose id all 
  printfn "%A" included }

Note that the code is all inside async block (because this keeps it asynchronous). It first creates a list of computations that return options with None (to skip restaurant) or Some (to include restaurant), then runs all of them and filters the None values using Seq.choose.

To implement this sequentially, you'd basically need your own implementation of filter that is wrapped in async blocks. This would be a good starting point (although it is not tail-recursive):

let rec filterAsync f items = async {
  match items with
  | [] -> return []
  | x::xs -> 
      let! included = f x
      let! rest = filterAsync f xs
      return (if included then x::rest else rest) }
like image 162
Tomas Petricek Avatar answered Feb 26 '26 20:02

Tomas Petricek



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!