(How) can I make this monadic bind tail-recursive? - f#
I have this monad called Desync -
[<AutoOpen>]
module DesyncModule =
/// The Desync monad. Allows the user to define in a sequential style an operation that spans
/// across a bounded number of events. Span is bounded because I've yet to figure out how to
/// make Desync implementation tail-recursive (see note about unbounded recursion in bind). And
/// frankly, I'm not sure if there is a tail-recursive implementation of it...
type [<NoComparison; NoEquality>] Desync<'e, 's, 'a> =
Desync of ('s -> 's * Either<'e -> Desync<'e, 's, 'a>, 'a>)
/// Monadic return for the Desync monad.
let internal returnM (a : 'a) : Desync<'e, 's, 'a> =
Desync (fun s -> (s, Right a))
/// Monadic bind for the Desync monad.
let rec internal bind (m : Desync<'e, 's, 'a>) (cont : 'a -> Desync<'e, 's, 'b>) : Desync<'e, 's, 'b> =
Desync (fun s ->
match (match m with Desync f -> f s) with
// ^--- NOTE: unbounded recursion here
| (s', Left m') -> (s', Left (fun e -> bind (m' e) cont))
| (s', Right v) -> match cont v with Desync f -> f s')
/// Builds the Desync monad.
type DesyncBuilder () =
member this.Return op = returnM op
member this.Bind (m, cont) = bind m cont
/// The Desync builder.
let desync = DesyncBuilder ()
It allows the implementation of game logic that executes across several game ticks to written in a seemingly sequential style using computation expressions.
Unfortunately, when used for tasks that last for an unbounded number of game ticks, it crashes with StackOverflowException. And even when it's not crashing, it's ending up with unwieldy stack traces like this -
InfinityRpg.exe!InfinityRpg.GameplayDispatcherModule.desync#525-20.Invoke(Nu.SimulationModule.Event<Prime.EitherModule.Either<Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit>,Nu.SimulationModule.Screen> _arg10) Line 530 F#
Prime.exe!Prime.DesyncModule.bind#20<Nu.SimulationModule.Event<Prime.EitherModule.Either<Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit>,Nu.SimulationModule.Screen>,Nu.SimulationModule.World,Microsoft.FSharp.Core.Unit,Nu.SimulationModule.Event<Prime.EitherModule.Either<Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit>,Nu.SimulationModule.Screen>>.Invoke(Nu.SimulationModule.World s) Line 24 F#
Prime.exe!Prime.DesyncModule.bind#20<Nu.SimulationModule.Event<Prime.EitherModule.Either<Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit>,Nu.SimulationModule.Screen>,Nu.SimulationModule.World,Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit>.Invoke(Nu.SimulationModule.World s) Line 21 F#
Prime.exe!Prime.DesyncModule.bind#20<Nu.SimulationModule.Event<Prime.EitherModule.Either<Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit>,Nu.SimulationModule.Screen>,Nu.SimulationModule.World,Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit>.Invoke(Nu.SimulationModule.World s) Line 21 F#
Prime.exe!Prime.DesyncModule.bind#20<Nu.SimulationModule.Event<Prime.EitherModule.Either<Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit>,Nu.SimulationModule.Screen>,Nu.SimulationModule.World,Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit>.Invoke(Nu.SimulationModule.World s) Line 21 F#
Prime.exe!Prime.DesyncModule.bind#20<Nu.SimulationModule.Event<Prime.EitherModule.Either<Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit>,Nu.SimulationModule.Screen>,Nu.SimulationModule.World,Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit>.Invoke(Nu.SimulationModule.World s) Line 21 F#
Prime.exe!Prime.DesyncModule.bind#20<Nu.SimulationModule.Event<Prime.EitherModule.Either<Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit>,Nu.SimulationModule.Screen>,Nu.SimulationModule.World,Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit>.Invoke(Nu.SimulationModule.World s) Line 21 F#
Prime.exe!Prime.DesyncModule.bind#20<Nu.SimulationModule.Event<Prime.EitherModule.Either<Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit>,Nu.SimulationModule.Screen>,Nu.SimulationModule.World,Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit>.Invoke(Nu.SimulationModule.World s) Line 21 F#
Prime.exe!Prime.DesyncModule.bind#20<Nu.SimulationModule.Event<Prime.EitherModule.Either<Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit>,Nu.SimulationModule.Screen>,Nu.SimulationModule.World,Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit>.Invoke(Nu.SimulationModule.World s) Line 21 F#
Prime.exe!Prime.Desync.step<Nu.SimulationModule.Event<Prime.EitherModule.Either<Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit>,Nu.SimulationModule.Screen>,Nu.SimulationModule.World,Microsoft.FSharp.Core.Unit>(Prime.DesyncModule.Desync<Nu.SimulationModule.Event<Prime.EitherModule.Either<Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit>,Nu.SimulationModule.Screen>,Nu.SimulationModule.World,Microsoft.FSharp.Core.Unit> m, Nu.SimulationModule.World s) Line 71 F#
Prime.exe!Prime.Desync.advanceDesync<Nu.SimulationModule.Event<Prime.EitherModule.Either<Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit>,Nu.SimulationModule.Screen>,Nu.SimulationModule.World,Microsoft.FSharp.Core.Unit>(Microsoft.FSharp.Core.FSharpFunc<Nu.SimulationModule.Event<Prime.EitherModule.Either<Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit>,Nu.SimulationModule.Screen>,Prime.DesyncModule.Desync<Nu.SimulationModule.Event<Prime.EitherModule.Either<Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit>,Nu.SimulationModule.Screen>,Nu.SimulationModule.World,Microsoft.FSharp.Core.Unit>> m, Nu.SimulationModule.Event<Prime.EitherModule.Either<Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit>,Nu.SimulationModule.Screen> e, Nu.SimulationModule.World s) Line 75 F#
Nu.exe!Nu.Desync.advance#98<Prime.EitherModule.Either<Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit>,Nu.SimulationModule.Screen>.Invoke(Nu.SimulationModule.Event<Prime.EitherModule.Either<Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit>,Nu.SimulationModule.Screen> event, Nu.SimulationModule.World world) Line 100 F#
Nu.exe!Nu.Desync.subscription#104-16<Prime.EitherModule.Either<Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit>,Nu.SimulationModule.Screen>.Invoke(Nu.SimulationModule.Event<Prime.EitherModule.Either<Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit>,Nu.SimulationModule.Screen> event, Nu.SimulationModule.World world) Line 105 F#
Nu.exe!Nu.World.boxableSubscription#165<Prime.EitherModule.Either<Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit>,Nu.SimulationModule.Screen>.Invoke(object event, Nu.SimulationModule.World world) Line 166 F#
I am hoping to solve the problem by making the Left case of the bind function tail-recursive. However, I'm not sure of two things -
1) if it can be done at all, and
2) how it would actually be done.
If it's impossible to make bind tail-recursive here, is there some way to restructure my monad to allow it to become tail-recursive?
EDIT 3 (subsumes previous edits): Here is additional code that implements the desync combinators I will use to demonstrate the stack overflow -
module Desync =
/// Get the state.
let get : Desync<'e, 's, 's> =
Desync (fun s -> (s, Right s))
/// Set the state.
let set s : Desync<'e, 's, unit> =
Desync (fun _ -> (s, Right ()))
/// Loop in a desynchronous context while 'pred' evaluate to true.
let rec loop (i : 'i) (next : 'i -> 'i) (pred : 'i -> 's -> bool) (m : 'i -> Desync<'e, 's, unit>) =
desync {
let! s = get
do! if pred i s then
desync {
do! m i
let i = next i
do! loop i next pred m }
else returnM () }
/// Loop in a desynchronous context while 'pred' evaluates to true.
let during (pred : 's -> bool) (m : Desync<'e, 's, unit>) =
loop () id (fun _ -> pred) (fun _ -> m)
/// Step once into a desync.
let step (m : Desync<'e, 's, 'a>) (s : 's) : 's * Either<'e -> Desync<'e, 's, 'a>, 'a> =
match m with Desync f -> f s
/// Run a desync to its end, providing e for all its steps.
let rec runDesync (m : Desync<'e, 's, 'a>) (e : 'e) (s : 's) : ('s * 'a) =
match step m s with
| (s', Left m') -> runDesync (m' e) e s'
| (s', Right v) -> (s', v)
Here is the Either implementation -
[<AutoOpen>]
module EitherModule =
/// Haskell-style Either type.
type Either<'l, 'r> =
| Right of 'r
| Left of 'l
And finally, here's simple a line of code that will yield a stack overflow -
open Desync
ignore <| runDesync (desync { do! during (fun _ -> true) (returnM ()) }) () ()
It seems to me your monad is a State with error handling.
It's basically ErrorT< State<'s,Either<'e,'a>>> but the error branch binds again which is not very clear to me why.
Anyway I was able to reproduce your Stack Overflow with a basic State monad:
type State<'S,'A> = State of ('S->('A * 'S))
module State =
let run (State x) = x :'s->_
let get() = State (fun s -> (s , s)) :State<'s,_>
let put x = State (fun _ -> ((), x)) :State<'s,_>
let result a = State(fun s -> (a, s))
let bind (State m) k = State(fun s ->
let (a, s') = m s
let (State u) = (k a)
u s') :State<'s,'b>
type StateBuilder() =
member this.Return op = result op
member this.Bind (m, cont) = bind m cont
let state = StateBuilder()
let rec loop (i: 'i) (next: 'i -> 'i) (pred: 'i -> 's -> bool) (m: 'i -> State<'s, unit>) =
state {
let! s = get()
do! if pred i s then
state {
do! m i
let i = next i
do! loop i next pred m }
else result () }
let during (pred : 's -> bool) (m : State<'s, unit>) =
loop () id (fun _ -> pred) (fun _ -> m)
// test
open State
ignore <| run (state { do! during (fun c -> true) (result ()) }) () // boom
As stated in the comments one way to solve this is to use a StateT<'s,Cont<'r,'a>>.
Here's an example of the solution. At the end there is a test with the zipIndex function which blows the stack as well when defined with a normal State monad.
Note you don't need to use the Monad Transformers from FsControl (now FSharpPlus), I use them because it's easier for me since I write less code but you can always create your transformed monad by hand.
Related
Railway Oriented Programming and partial application
I like using ROP when I have to deal with IO/Parsing strings/... However let's say that I have a function taking 2 parameters. How can you do clean/readable partial application when your 2 parameters are already a Result<'a,'b> (not necessary same 'a, 'b)? For now, what I do is that I use tuple to pass parameters and use the function below to get a Result of a tuple so I can then bind my function with this "tuple-parameter". /// Transform a tuple of Result in a Result of tuple let tupleAllResult x = match (fst x, snd x) with | Result.Ok a, Result.Ok b -> (a,b) |> Result.Ok | Result.Ok a, Result.Error b -> b |> Result.Error | Result.Error a, _ -> a |> Result.Error let f (a: 'T, b: 'U) = // something (A, B) |> tupleAllResult |> (Result.bind f) Any good idea? Here what I wrote, which works but might not be the most elegant let resultFunc (f: Result<('a -> Result<'b, 'c>), 'd>) a = match f with | Result.Ok g -> (g a) |> Result.Ok |> Result.flatten | Result.Error e -> e |> Result.Error |> Result.flatten
I am not seeing partial application in your example, a concept related to currying and argument passing -- that's why I am assuming that you are after the monadic apply, in that you want to transform a function wrapped as a Result value into a function that takes a Result and returns another Result. let (.>>.) aR bR = // This is "tupleAllResult" under a different name match aR, bR with | Ok a, Ok b -> Ok(a, b) | Error e, _ | _, Error e -> Error e // val ( .>>. ) : aR:Result<'a,'b> -> bR:Result<'c,'b> -> Result<('a * 'c),'b> let (<*>) fR xR = // This is another name for "apply" (fR .>>. xR) |> Result.map (fun (f, x) -> f x) // val ( <*> ) : fR:Result<('a -> 'b),'c> -> xR:Result<'a,'c> -> Result<'b,'c> The difference to what you have in your question is map instead of bind in the last line. Now you can start to lift functions into the Result world: let lift2 f xR yR = Ok f <*> xR <*> yR // val lift2 : // f:('a -> 'b -> 'c) -> xR:Result<'a,'d> -> yR:Result<'b,'d> -> Result<'c,'d> let res : Result<_,unit> = lift2 (+) (Ok 1) (Ok 2) // val res : Result<int,unit> = Ok 3
Church encoded Free monad in F#
I am trying to express the Church encoding of the Free monad in F#. Free is specialized to a particular functor, Effect. I am able to write both return_ : 'T -> Free<'T> and bind: ('T -> Free<'U>) -> Free<'T> -> Free<'U> without any problems. A sketch of my implementation is given below. type Effect<'T> = GetStr of (string -> 'T) | PutStr of string * 'T module Effect = let map (f: 'a -> 'b) : Effect<'a> -> Effect<'b> = function | GetStr k -> GetStr(f << k) | PutStr (s,t) -> PutStr(s, f t) type Free<'T> = abstract Apply : ('T -> 'R) -> (Effect<'R> -> 'R) -> 'R module Free = let inline runFree (f:Free<'T>) (kp: 'T -> 'R) (kf: Effect<'R> -> 'R) : 'R = f.Apply kp kf let return_ (x: 'a) : Free<'a> = { new Free<'a> with member __.Apply kp _ = kp x } let bind (f: 'a -> Free<'b>) (m: Free<'a>) : Free<'b> = { new Free<'b> with member __.Apply kp kf = runFree m (fun a -> runFree (f a) kp kf ) kf } When I try to write an interpreter for this encoding, I hit a problem. Given the following code: module Interpret = let interpretEffect = function | GetStr k -> let s = System.Console.ReadLine() (k s , String.length s) | PutStr(s,t) -> do System.Console.WriteLine s (t , 0) let rec interpret (f: Free<string * int>) = Free.runFree f (fun (str,len) -> (str,len)) (fun (a: Effect<Free<string*int>>) -> let (b,n) = interpretEffect a let (c,n') = interpret b (c, n + n') ) I get a type error in the third argument to Free.runFree within the interpret function: ... (fun (a: Effect<Free<string*int>>) -> ^^^^^^^^^^^^^^^^^^ ------ Expecting a Effect<string * int> but given a Effect<Free<string*int>> I understand why this is happening (the result type of the first function determines 'R === string*int) and suspect that can be solved using a rank-2 function (which can be encoded in F# e.g. http://eiriktsarpalis.github.io/typeshape/#/33) but I am not sure how to apply it. Any pointers would be much appreciated. Michael
You do not need to do anything there, the compiler suggested type is in fact correct (and in line with the type of runFree). It seems that what you're thinking of there is Scott encoding (ripped from this Haskell question): runFree :: Functor f => (a -> r) -> (f (F f a) -> r) -> F f a -> r where F f a would be your Effect-specialised Free<'a>, and f (F f a) would be Effect<Free<'a>>, which is what you're trying to use. Whereas Church encoding would be: runFree :: Functor f => (a -> r) -> (f r -> r) -> F f a -> r where f r is Effect<'a> - thus making it easier to express in F# (which is why I assume you're using it in the first place. This is what I had for interpret: let rec interpret (f: Free<string * int>) = Free.runFree f (fun (str,len) -> (str,len)) (fun (a: Effect<_>) -> let (b,n) = interpretEffect a let (c,n') = interpret (Free.pureF b) (c, n + n') ) where pureF is let pureF (x: 'a) : Free<'a> = { new Free<'a> with member __.Apply kp _ = kp x } i.e. your return_ function. I think defining the corresponding freeF function would clear some things (like why is Effect<'a> a functor - you're not making use of this fact anywhere in the code you pasted).
Recursively update a State Monad
this question is related to this question I have a state monad. An object provides an update function as in the OOD strategy pattern. The choice of having a object is that in real, production code, the class provides an array of operations, all sharing state through the monad. Inheritance helped me extend the basic functionality and further customizing the class providing the operations. The choice of having a monad instead of a mutable property within the class is that the monad, through proper use of generics, is helping me abstracting and being more flexible on what variables/information must be carried along the computation as "state". I have a simple toy example: ///////////////////////////////////////////////////////////////////////////////////// // Definition of the state ///////////////////////////////////////////////////////////////////////////////////// type StateFunc<'State, 'T> = 'State -> 'T * 'State ///////////////////////////////////////////////////////////////////////////////////// // Definition of the State monad type ///////////////////////////////////////////////////////////////////////////////////// type StateMonadBuilder<'State>() = // M<'T> -> M<'T> member b.ReturnFrom a : StateFunc<'State, 'T> = a // 'T -> M<'T> member b.Return a : StateFunc<'State, 'T> = ( fun s -> a, s) // M<'T> * ('T -> M<'U>) -> M<'U> member b.Bind(p : StateFunc<_, 'T>, rest : 'T -> StateFunc<_,_>) : StateFunc<'State, 'U> = (fun s -> let a, s' = p s rest a s') // Getter for the whole state, this type signature is because it passes along the state & returns the state member b.getState : StateFunc<'State, _> = (fun s -> s, s) // Setter for the state member b.putState (s:'State) : StateFunc<'State, _> = (fun _ -> (), s) let runState f init = f init ///////////////////////////////////////////////////////////////////////////////////// // STRATEGY PATTERN ///////////////////////////////////////////////////////////////////////////////////// let state = StateMonadBuilder<int> () // DoubleFunctOne defines standard operations that remain always the same type Strategy (aFunction) = member this.Update (x: int) = state { let! currState = state.getState let processedx = aFunction x do! state.putState (currState + x) } // Create a function that customizes the strategy let myFunction x = 2 * x // Customize the strategy with the desired function: let strategy = Strategy (myFunction) ///////////////////////////////////////////////////////////////////////////////////////////////////////// // Update recursively ///////////////////////////////////////////////////////////////////////////////////////////////////////// // ?? How to run update recursively ?? let result initialCondition = initialCondition |> (for i = 10 to 100 do yield state { do! strategy.Update i } ) My goal is to apply the initial conditions, fetch data and launch recursively (within a for or a while loop or even some functional operation) the functions provided by strategy. Working with the monad, I am not sure how to do this. Thank you. Computational Expression For Inspired by #kvb answer, I have added a for method to the computational expression. // Loops through seqnc of numbers that constitute an input to func member b.For (seqnc:_ List, func) = seqnc |> List.map (fun item -> func item) |> List.reduce (fun acc item -> (fun s -> let _, s' = acc s item s' ) ) I run a few tests and I have the impression that this one works. Thanks.
Something like this? let result initialCondition = let rec loop = function | 101 -> state { return () } | i -> state { do! strategy.Update i do! loop (i+1) } initialCondition |> runState (loop 10) Alternatively, define a For member on your builder and write it the more imperative way: let result initialCondition = let f = state { for i in 10 to 100 do do! strategy.Update i } initialCondition |> runState f Also, note that there is likely a bug in your definition of Strategy.Update: processedx is bound but unused.
What is wrong with 100000 factorial using ContinuationMonad?
It is powerful technique using recursion because its strong describable feature. Tail recursion provides more powerful computation than normal recursion because it changes recursion into iteration. Continuation-Passing Style (CPS) can change lots of loop codes into tail recursion. Continuation Monad provides recursion syntax but in essence it is tail recursion, which is iteration. It is supposed to reasonable use Continuation Monad for 100000 factorial. Here is the code. type ContinuationBuilder() = member b.Bind(x, f) = fun k -> x (fun x -> f x k) member b.Return x = fun k -> k x member b.ReturnFrom x = x (* type ContinuationBuilder = class new : unit -> ContinuationBuilder member Bind : x:(('d -> 'e) -> 'f) * f:('d -> 'g -> 'e) -> ('g -> 'f) member Return : x:'b -> (('b -> 'c) -> 'c) member ReturnFrom : x:'a -> 'a end *) let cont = ContinuationBuilder() //val cont : ContinuationBuilder let fac n = let rec loop n = cont { match n with | n when n = 0I -> return 1I | _ -> let! x = fun f -> f n let! y = loop (n - 1I) return x * y } loop n (fun x -> x) let x2 = fac 100000I There is wrong message: "Process is terminated due to StackOverflowException." What is wrong with 100000 factorial using ContinuationMonad?
You need to compile the project in Release mode or check the "Generate tail calls" option in project properties (or use --tailcalls+ if you're running the compiler via command line). By default, tail call optimization is not enabled in Debug mode. The reason is that, if tail-calls are enabled, you will not see as useful information about stack traces. So, disabling them by default gives you more pleasant debugging experience (even in Debug mode, the compiler optimizes tail-recursive functions that call themselves, which handles most situations).
You probably need to add this memeber to your monad builder: member this.Delay(mk) = fun c -> mk () c
Is this a reasonable foundation for a parser combinator library?
I've been working with FParsec lately and I found that the lack of generic parsers is a major stopping point for me. My goal for this little library is simplicity as well as support for generic input. Can you think of any additions that would improve this or is anything particularly bad? open LazyList type State<'a, 'b> (input:LazyList<'a>, data:'b) = member this.Input = input member this.Data = data type Result<'a, 'b, 'c> = | Success of 'c * State<'a, 'b> | Failure of string * State<'a, 'b> type Parser<'a,'b, 'c> = State<'a, 'b> -> Result<'a, 'b, 'c> let (>>=) left right state = match left state with | Success (result, state) -> (right result) state | Failure (message, _) -> Result<'a, 'b, 'd>.Failure (message, state) let (<|>) left right state = match left state with | Success (_, _) as result -> result | Failure (_, _) -> right state let (|>>) parser transform state = match parser state with | Success (result, state) -> Success (transform result, state) | Failure (message, _) -> Failure (message, state) let (<?>) parser errorMessage state = match parser state with | Success (_, _) as result -> result | Failure (_, _) -> Failure (errorMessage, state) type ParseMonad() = member this.Bind (f, g) = f >>= g member this.Return x s = Success(x, s) member this.Zero () s = Failure("", s) member this.Delay (f:unit -> Parser<_,_,_>) = f() let parse = ParseMonad() Backtracking Surprisingly it didn't take too much code to implement what you describe. It is a bit sloppy but seems to work quite well. let (>>=) left right state = seq { for res in left state do match res with | Success(v, s) -> let v = right v s |> List.tryFind ( fun res -> match res with | Success (_, _) -> true | _ -> false ) match v with | Some v -> yield v | None -> () } |> Seq.toList let (<|>) left right state = left state # right state Backtracking Part 2 Switched around the code to use lazy lists and tail-call optimized recursion. let (>>=) left right state = let rec readRight lst = match lst with | Cons (x, xs) -> match x with | Success (r, s) as q -> LazyList.ofList [q] | Failure (m, s) -> readRight xs | Nil -> LazyList.empty<Result<'a, 'b, 'd>> let rec readLeft lst = match lst with | Cons (x, xs) -> match x with | Success (r, s) -> match readRight (right r s) with | Cons (x, xs) -> match x with | Success (r, s) as q -> LazyList.ofList [q] | Failure (m, s) -> readRight xs | Nil -> readLeft xs | Failure (m, s) -> readLeft xs | Nil -> LazyList.empty<Result<'a, 'b, 'd>> readLeft (left state) let (<|>) (left:Parser<'a, 'b, 'c>) (right:Parser<'a, 'b, 'c>) state = LazyList.delayed (fun () -> left state) |> LazyList.append <| LazyList.delayed (fun () -> right state)
I think that one important design decision that you'll need to make is whether you want to support backtracking in your parsers or not (I don't remember much about parsing theory, but this probably specifies the types of languages that your parser can handle). Backtracking. In your implementation, a parser can either fail (the Failure case) or produce exactly one result (the Success case). An alternative option is to generate zero or more results (for example, represent results as seq<'c>). Sorry if this is something you already considered :-), but anyway... The difference is that your parser always follows the first possible option. For example, if you write something like the following: let! s1 = (str "ab" <|> str "a") let! s2 = str "bcd" Using your implementation, this will fail for input "abcd". It will choose the first branch of the <|> operator, which will then process first two characters and the next parser in the sequence will fail. An implementation based on sequences would be able to backtrack and follow the second path in <|> and parse the input. Combine. Another idea that occurs to me is that you could also add Combine member to your parser computation builder. This is a bit subtle (because you need to understand how computation expressions are translated), but it can be sometimes useful. If you add: member x.Combine(a, b) = a <|> b member x.ReturnFrom(p) = p You can then write recursive parsers nicely: let rec many p acc = parser { let! r = p // Parse 'p' at least once return! many p (r::acc) // Try parsing 'p' multiple times return r::acc |> List.rev } // If fails, return the result