I\'m writing a brainfuck interpreter in Haskell, and I came up with what I believe to be a very interesting description of a program:
data Program m = Instructio
Forwards traveling state with a continuation monad looks like this:
Cont (fw -> r) a
Then the type of the argument to cont
is
(a -> fw -> r) -> fw -> r
So you get a fw
passed in from the past which you have to pass on to the continuation.
Backwards traveling state looks like this:
Cont (bw, r) a
Then the type of the argument to cont
is
(a -> (bw, r)) -> (bw, r)
I.e. you get a bw
from the continuation which you have to pass on to the past.
These can be combined into one continuation monad:
Cont (fw -> (bw, r)) a
There's a catch when applying this to your parser, because toProgramStep
builds the program in reverse, so the list of ']' points is the forward state, and the list of '[' points is the backward state. Also, I got lazy and skipped the Maybe part, which should catch the pattern matching errors in openBrace
and closeBrace
.
type ParseState = Cont ([TapeP] -> ([TapeP], TapeP))
toProgram :: String -> TapeP
toProgram = snd . ($ []) . (`runCont` (\a _ -> ([], a))) . toProgramStep
openBrace :: ParseState TapeP -> ParseState TapeP
openBrace mcontinue = do
continue <- mcontinue
cont $ \k (break:bs) -> let (cs, r) = k (loopControl continue break) bs in (continue:cs, r)
closeBrace :: ParseState TapeP -> ParseState TapeP
closeBrace mbreak = do
break <- mbreak
cont $ \k bs -> let (continue:cs, r) = k (loopControl continue break) (break:bs) in (cs, r)
Being terribly lazy with this answer since I'm not comfortable with Cont
, but is MonadFix perhaps what you're looking for? State
is an instance, though not Cont
, and it lets you do things that look like (using "recursive do" notation):
{-# LANGUAGE DoRec #-}
parseInst str = do
rec ctl <- parseInstructionsLinkingTo ctl str
This was the solution I discovered for my actors library: we want a spawn
operation that returns the spawned actor's mailbox, but then how can we launch mutually-communicating actors? Or an actor with access to its own mailbox?
With a suitable MonadFix
instance we can do:
fork3 = do
rec mb1 <- spawn $ actorSpamming mb2 mb3
mb2 <- spawn $ actorSpamming mb1 mb2
mb3 <- spawn $ actorSpamming mb2 mb3
send "go" mb1
Hope above gives you ideas.