Here is something that is almost a direct translation of the same solution strategy.
However, I think there might be a better / simpler choice, I am still pondering it.
type Runner = int -> Result and Result = Result of int * option<Runner> let AddOne = fun x -> Result(x+1, None) let Idle = fun x -> Result(x, None) let rec Pair(r1,r2) = fun x -> match r1 x with | Result(data,None) -> Result(data, Some(r2)) | Result(data,Some(next)) -> Result(data,Some(Pair(next,r2))) let rec Repeat rn = fun x -> if n = 0 then rx else match rx with | Result(data,None) -> Result(data, Some(Repeat r (n-1))) | Result(data,Some(next)) -> Result(data, Some(Pair(next, Repeat r (n-1))))
EDIT
Here is another way that is a bit more sophisticated ... I'm still trying to see if there is a good way to work in the "list" as the results seem to be isomorphic to cons cells ...
type Runner = Runner of (int -> int * option<Runner>) let AddOne = Runner(fun x -> x+1, None) let Idle = Runner(fun x -> x, None) let rec Pair(Runner(r1),R2) = Runner(fun x -> match r1 x with | data,None -> data, Some(R2) | data,Some(next) -> data, Some(Pair(next,R2))) let rec Repeat (Runner(r) as R) n = Runner(fun x -> if n = 0 then rx else match rx with | data,None -> data, Some(Repeat R (n-1)) | data,Some(next) -> data, Some(Pair(next, Repeat R (n-1))))
EDIT
Another version, it uses lists, but now I feel it is strange here ...
type Runner = Runner of (int -> int * list<Runner>) let AddOne = Runner(fun x -> x+1, []) let Idle = Runner(fun x -> x, []) let rec Pair(Runner(r1),R2) = Runner(fun x -> match r1 x with | data,xs -> data, xs @ [R2]) // inefficient let rec Repeat (Runner(r) as R) n = Runner(fun x -> if n = 0 then rx else match rx with | data,xs -> data, xs @ List.init (n-1) (fun _ -> R)) // inefficient
It is almost the same as an “action queue”, a list of functions int->int . But each guy can create some “suffix actions” that start immediately after him (but before remaining work in the potential queue), and trying to maintain ordering with a purely functional data structure is potentially inefficient (without the right tree / queue library at hand). It would be interesting to know how this will be used / consumed, as perhaps a small change can lead to a completely different strategy.