0
\$\begingroup\$

I've been writing a bunch of helper functions lately, all revolving around the concept of an early exit fold (which is a bit like unfold, i.e. you can control when the looping ends but you pass a list of items).

A dummy example:

[<EntryPoint>]
let main args = 
    let guidFrom i = Guid(0, 0s, 0s, 0uy, 0uy, 0uy, 0uy, 0uy, 0uy, 0uy, i)
    let processTokenRequest i previous tokenRequest = task {
        do! Task.Delay(500)
        let b = i |> uint8 |> guidFrom
        if i < 4 then return (Ok { Access = b; Refresh = b }) else return Error $"meh {i}"
    }
    
    let initRequest = Ok { Access = guidFrom 0uy; Refresh = guidFrom 0uy }
    
    [ 1 .. 10]
    |> List.mapi (fun i _ -> { Id = i })
    |> ResultList.earlyExitResultFoldITask processTokenRequest initRequest
    |> Task.GetAwaiterResult
    |> printfn "%A"
    0

Anyway, all my helpers seem to work as expected, but they are all "way" too similar in their respective implementations except to a few details: sync / task / async variants as well as the relevant desired inputs / outputs needed for both options, results, lists and sequences.

My issue here is that I've found that the overall implementation for the all cases is quite verbose to say the least. I would to have some reusable part which at first seems like obvious considering the degree of similarity among the different bits of implementation, but in practice I can't find a proper way to achieve that.

Any idea?


OptionList:

[<RequireQualifiedAccess>]
module FSharpPlayground.OptionList


let rec earlyExitOptionFold folder state source =
    match state, source with
    | Some value, [] ->
        Some value
    | Some value, head :: tail ->
        let intermediateState = folder value head
        earlyExitOptionFold folder intermediateState tail
    | None, _ ->
        None
        
let earlyExitOptionFoldI folder state source =
    let rec core folder i state source =
        match state, source with
        | Some value, [] ->
            Some value
        | Some value, head :: tail ->
            let intermediateState = folder i value head
            core folder i intermediateState tail
        | None, _ ->
            None
    core folder 0 state source 

let rec earlyExitOptionFoldTask folder state source =
    task {
        match state, source with
        | Some value, [] ->
            return Some value
        | Some value, head :: tail ->
            let! intermediateState = folder value head
            return! earlyExitOptionFoldTask folder intermediateState tail
        | None, _ ->
            return None    
    }

let earlyExitOptionFoldITask folder state source =
    let rec core folder i state source =
        task {
            match state, source with
            | Some value, [] ->
                return Some value
            | Some value, head :: tail ->
                let! intermediateState = folder value head
                return! core folder (i + 1) intermediateState tail
            | None, _ ->
                return None    
        }
    core folder 0 state source 
        
let rec earlyExitOptionFoldAsync folder state source =
    async {
        match state, source with
        | Some value, [] ->
            return Some value
        | Some value, head :: tail ->
            let! intermediateState = folder value head
            return! earlyExitOptionFoldAsync folder intermediateState tail
        | None, _ ->
            return None    
    }

let earlyExitOptionFoldIAsync folder state source =
    let rec core folder i state source =
        async {
            match state, source with
            | Some value, [] ->
                return Some value
            | Some value, head :: tail ->
                let! intermediateState = folder value head
                return! core folder (i + 1) intermediateState tail
            | None, _ ->
                return None    
        }
    core folder 0 state sourc


ResultList.fs:

[<RequireQualifiedAccess>]
module FSharpPlayground.ResultList


let rec earlyExitResultFold folder state source =
    match state, source with
    | Ok ok, [] ->
        Ok ok
    | Ok ok, head :: tail ->
        let intermediateState = folder ok head
        earlyExitResultFold folder intermediateState tail
    | Error error, _ ->
        Error error
        
let earlyExitResultFoldI folder state source =
    let rec core folder i state source =
        match state, source with
        | Ok ok, [] ->
            Ok ok
        | Ok ok, head :: tail ->
            let intermediateState = folder i ok head
            core folder (i + 1) intermediateState tail
        | Error error, _ ->
            Error error
    core folder 0 state source 

let rec earlyExitResultFoldTask folder state source =
    task {
        match state, source with
        | Ok ok, [] ->
            return Ok ok
        | Ok ok, head :: tail ->
            let! intermediateState = folder ok head
            return! earlyExitResultFoldTask folder intermediateState tail
        | Error error, _ ->
            return Error error    
    }
    
let earlyExitResultFoldITask folder state source =
    let rec core folder i state source =
        task {
            match state, source with
            | Ok ok, [] ->
                return Ok ok
            | Ok ok, head :: tail ->
                let! intermediateState = folder i ok head
                return! core folder (i + 1) intermediateState tail
            | Error error, _ ->
                return Error error    
        }
    core folder 0 state source 
    
let rec earlyExitResultFoldAsync folder state source =
    async {
        match state, source with
        | Ok ok, [] ->
            return Ok ok
        | Ok ok, head :: tail ->
            let! intermediateState = folder ok head
            return! earlyExitResultFoldAsync folder intermediateState tail
        | Error error, _ ->
            return Error error    
    }

let earlyExitResultFoldIAsync folder state source =
    let rec core folder i state source =
        async {
            match state, source with
            | Ok ok, [] ->
                return Ok ok
            | Ok ok, head :: tail ->
                let! intermediateState = folder i ok head
                return! core folder (i + 1) intermediateState tail
            | Error error, _ ->
                return Error error    
        }
    core folder 0 state source

OptionSeq:

[<RequireQualifiedAccess>]
module FSharpPlayground.OptionSeq


let earlyExitOptionFold folder state (source : seq<'T>)  =
    use e = source.GetEnumerator()
    let f = OptimizedClosures.FSharpFunc<_, _, _>.Adapt folder
    let mutable dontStopMeNow = true
    let mutable state = state
    while dontStopMeNow do
        match state, e.MoveNext() with
        | None, _ ->
            state <- None
            dontStopMeNow <- false
        | Some _, false ->
            dontStopMeNow <- false
        | Some value, true ->
            state <- f.Invoke(value, e.Current)
            dontStopMeNow <- true
    state
    
let earlyExitOptionFoldTask folder state (source : seq<'T>)  =
    task {
        use e = source.GetEnumerator()
        let f = OptimizedClosures.FSharpFunc<_, _, _>.Adapt folder
        let mutable dontStopMeNow = true
        let mutable state = state
        while dontStopMeNow do
            match state, e.MoveNext() with
            | None, _ ->
                state <- None
                dontStopMeNow <- false
            | Some _, false ->
                dontStopMeNow <- false
            | Some value, true ->
                let! newState = f.Invoke(value, e.Current)
                state <- newState
                dontStopMeNow <- true
        return state
    }

    
let earlyExitOptionFoldAsync folder state (source : seq<'T>)  =
    async {
        use e = source.GetEnumerator()
        let f = OptimizedClosures.FSharpFunc<_, _, _>.Adapt folder
        let mutable dontStopMeNow = true
        let mutable state = state
        while dontStopMeNow do
            match state, e.MoveNext() with
            | None, _ ->
                state <- None
                dontStopMeNow <- false
            | Some _, false ->
                dontStopMeNow <- false
            | Some value, true ->
                let! newState = f.Invoke(value, e.Current)
                state <- newState
                dontStopMeNow <- true
        return state
    }

let earlyExitOptionFoldI folder state (source : seq<'T>)  =
    use e = source.GetEnumerator()
    let f = OptimizedClosures.FSharpFunc<_, _, _, _>.Adapt folder
    let mutable dontStopMeNow = true
    let mutable state = state
    let mutable i = 0
    while dontStopMeNow do
        match state, e.MoveNext() with
        | None , _ ->
            state <- None
            dontStopMeNow <- false
        | Some _, false ->
            dontStopMeNow <- false
        | Some value, true ->
            state <- f.Invoke(i, value, e.Current)
            dontStopMeNow <- true
            i <- i + 1
    state

let earlyExitOptionFoldITask folder state (source : seq<'T>)  =
    task {
        use e = source.GetEnumerator()
        let f = OptimizedClosures.FSharpFunc<_, _, _, _>.Adapt folder
        let mutable dontStopMeNow = true
        let mutable state = state
        let mutable i = 0
        while dontStopMeNow do
            match state, e.MoveNext() with
            | None , _ ->
                state <- None
                dontStopMeNow <- false
            | Some _, false ->
                dontStopMeNow <- false
            | Some value, true ->
                state <- f.Invoke(i, value, e.Current)
                dontStopMeNow <- true
                i <- i + 1
        return state
    }

let earlyExitOptionFoldIAsync folder state (source : seq<'T>)  =
    async {
        use e = source.GetEnumerator()
        let f = OptimizedClosures.FSharpFunc<_, _, _, _>.Adapt folder
        let mutable dontStopMeNow = true
        let mutable state = state
        let mutable i = 0
        while dontStopMeNow do
            match state, e.MoveNext() with
            | None , _ ->
                state <- None
                dontStopMeNow <- false
            | Some _, false ->
                dontStopMeNow <- false
            | Some value, true ->
                state <- f.Invoke(i, value, e.Current)
                dontStopMeNow <- true
                i <- i + 1
        return state
    }

ResultSeq.fs:

[<RequireQualifiedAccess>]
module FSharpPlayground.ResultSeq


let earlyExitResultFold folder state (source : seq<'T>)  =
    use e = source.GetEnumerator()
    let f = OptimizedClosures.FSharpFunc<_, _, _>.Adapt folder
    let mutable dontStopMeNow = true
    let mutable state = state
    while dontStopMeNow do
        match state, e.MoveNext() with
        | Error error, _ ->
            state <- Error error
            dontStopMeNow <- false
        | Ok _, false ->
            dontStopMeNow <- false
        | Ok ok, true ->
            state <- f.Invoke(ok, e.Current)
            dontStopMeNow <- true
    state
    
let earlyExitResultFoldTask folder state (source : seq<'T>)  =
    task {
        use e = source.GetEnumerator()
        let f = OptimizedClosures.FSharpFunc<_, _, _>.Adapt folder
        let mutable dontStopMeNow = true
        let mutable state = state
        while dontStopMeNow do
            match state, e.MoveNext() with
            | Error error, _ ->
                state <- Error error
                dontStopMeNow <- false
            | Ok _, false ->
                dontStopMeNow <- false
            | Ok ok, true ->
                let! newState = f.Invoke(ok, e.Current)
                state <- newState
                dontStopMeNow <- true
        return state
    }

let earlyExitResultFoldAsync folder state (source: seq<'T>)  =
    async {
        use e = source.GetEnumerator()
        let f = OptimizedClosures.FSharpFunc<_, _, _>.Adapt folder
        let mutable dontStopMeNow = true
        let mutable state = state
        while dontStopMeNow do
            match state, e.MoveNext() with
            | Error error, _ ->
                state <- Error error
                dontStopMeNow <- false
            | Ok _, false ->
                dontStopMeNow <- false
            | Ok ok, true ->
                let! newState = f.Invoke(ok, e.Current)
                state <- newState
                dontStopMeNow <- true
        return state
    }
    
let earlyExitResultFoldI folder state (source : seq<'T>)  =
    use e = source.GetEnumerator()
    let f = OptimizedClosures.FSharpFunc<_, _, _, _>.Adapt folder
    let mutable dontStopMeNow = true
    let mutable state = state
    let mutable i = 0
    while dontStopMeNow do
        match state, e.MoveNext() with
        | Error error, _ ->
            state <- Error error
            dontStopMeNow <- false
        | Ok _, false ->
            dontStopMeNow <- false
        | Ok ok, true ->
            state <- f.Invoke(i, ok, e.Current)
            dontStopMeNow <- true
            i <- i + 1
    state
    
let earlyExitResultFoldITask folder state (source : seq<'T>)  =
    task {
        use e = source.GetEnumerator()
        let f = OptimizedClosures.FSharpFunc<_, _, _, _>.Adapt folder
        let mutable dontStopMeNow = true
        let mutable state = state
        let mutable i = 0
        while dontStopMeNow do
            match state, e.MoveNext() with
            | Error error, _ ->
                state <- Error error
                dontStopMeNow <- false
            | Ok _, false ->
                dontStopMeNow <- false
            | Ok ok, true ->
                let! newState = f.Invoke(i, ok, e.Current)
                state <- newState
                dontStopMeNow <- true
                i <- i + 1
        return state
    }

let earlyExitResultFoldIAsync folder state (source : seq<'T>)  =
    async {
        use e = source.GetEnumerator()
        let f = OptimizedClosures.FSharpFunc<_, _, _, _>.Adapt folder
        let mutable dontStopMeNow = true
        let mutable state = state
        let mutable i = 0
        while dontStopMeNow do
            match state, e.MoveNext() with
            | Error error, _ ->
                state <- Error error
                dontStopMeNow <- false
            | Ok _, false ->
                dontStopMeNow <- false
            | Ok ok, true ->
                let! newState = f.Invoke(i, ok, e.Current)
                state <- newState
                dontStopMeNow <- true
                i <- i + 1
        return state
    }
\$\endgroup\$
0

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.