简体   繁体   中英

Encapsulate a multi pass algorithm comprised of operations

I am trying to encapsulate the idea of "multiple passes" of a set of operations.

The idea is that if I run a set of operations, they will produce a new set of operations, those will get run, producing a third set which are then run, and so on. Obviously, there is the danger the algorithm won't terminate, but who cares.

I tried to model it with:

type Op = Unit -> Op option

let rec performPass (ops : Op list) : Unit =
    ops |> List.choose (fun x -> x ()) |> performPass

The idea was that I could write functions like:

let firstPass () =
    // Perform state-changing first-pass
    Some (fun () ->
        // Perform state-changing second-pass
        Some (fun () ->
            // Third pass is the last
            None))

But was quickly informed that this is an infinite type. Upon reflection, I agreed that it's infinite but struggled to find another way to represent this.

So, how would you encapsulate a multi pass algorithm where the previous pass generates the code for the next pass?

The F# compiler does not like immediate recursion in type aliases , but it will happily let you define a recursive type when you define it as a new type - for example, using single-case discriminated union:

type Op = Unit -> Op option             // Type alias does not work
type Op = Op of (Unit -> Op option)     // Single-case union is fine

Then you can update your functions as follows:

let rec performPass (ops : Op list) : Unit =
    ops |> List.choose (fun (Op x) -> x ()) |> performPass

Here, the only change is that I turned fun x -> x () to fun (Op x) -> x () which uses pattern matching on the single case to extract the function from the Op type.

let firstPass () =
    // Perform state-changing first-pass
    Some (Op (fun () ->
        // Perform state-changing second-pass
        Some (Op (fun () ->
            // Third pass is the last
            None))))

Here, I added Op in a number of places to wrap the function inside the Op type.

This is not the general solution, but one fix is to simply limit it to 3 passes:

type Op1 = Unit -> Op2
and Op2 = Unit -> Op3
and Op3 = Unit -> Unit

let rec performPasses (ops1 : Op1 list) : Unit =
    let apply x = x ()
    ops1 |> List.map apply |> List.map apply |> List.map apply |> ignore

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM