Search code examples
f#async-workflow

How do I use an async workflow in a Seq.pick in F#


I am new to functional programming in general and started learning F# recently. I wanted to use an async workflow returning Async<'U option> to pick an item in a Sequence. I find a nice Seq.pick function, but I am not sure how I could use that with an async workflow.

If that is not possible, is there another alternative to using an imperative style program to pick the item from the list. The following is a modified variation of my program. Any feedback is highly appreciated.

let run = async {
    while not stopped do
        use! resource = acquireResourceLockAsync
        let! items = fetchItemsAsync 5
        let! item = Seq.pick returnIfLocked items
        let! status = performTaskAsync item
        do! updateStatusAsync status
        do! Async.Sleep 1000
    }

Thanks in anticipation.

EDIT: Updated my question based on the answer by jpalmer. I noticed both Seq.filter and Seq.pick earlier and decided that Seq.pick will meet my need better, as I need the first item that I am able to lock. However, I forgot to change the return value of my function - instead of returning true, it should return Some(item). Now with that update, is there an elegant way to approach this without 1) blocking a thread to convert Async<'U option> to 'U and 2) resorting to an imperative style looping?


Solution

  • I am unclear exactly what you are trying to do. If you want to convert from Async<'T> to 'T non-blocking, then you want to use let! in an async workflow. So the seq-like logic probably needs to be written as its own loop, as suggested below. If that doesn't help, then perhaps share more code, especially the intended types of items/item/returnIfLocked, as I'm unclear what's async in your example.

    let asyncPick f (s:seq<_>) =
        async {
            use e = s.GetEnumerator()
            let r = ref None
            while Option.isNone(!r) && e.MoveNext() do
                let! x = f e.Current
                r := x
            match !r with
            | Some z -> return z
            | None -> return failwith "no matching item found"
        }
    
    let chooser ax =
        async {
            let! x = ax
            if x%3 = 0 then
                return Some x
            else
                return None
        }
    
    let s = seq { for i in 1..10 do yield async { return i } }
    
    let main() =
        async {
            let! firstChosen = s |> asyncPick chooser
            return firstChosen
        } 
        |> Async.RunSynchronously
        |> printfn "%d"
    
    main()