Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Cont to acquire values from the future and the past

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 = Instruction (m ()) (Program m)
               | Control (m (Program m))
               | Halt

However, it's tricky to parse a textual representation of a brainfuck program into this data type. The problem arises with trying to correctly parse square brackets, because there is some knot-tying to do so that the final Instruction inside a loop links to the loop's Control again.

A bit more preliminary information. See this version on the github repo for all the details.

type TapeM = StateT Tape IO
type TapeP = Program TapeM
type TapeC = Cont TapeP

branch :: Monad m => m Bool -> Program m -> Program m -> Program m
branch cond trueBranch falseBranch =
  Control ((\b -> if b then trueBranch else falseBranch) `liftM` cond)

loopControl :: TapeP -> TapeP -> TapeP
loopControl = branch (not <$> is0)

Here's what I tried:

toProgram :: String -> TapeP
toProgram = (`runCont` id) . toProgramStep

liftI :: TapeM () -> String -> TapeC TapeP
liftI i cs = Instruction i <$> toProgramStep cs

toProgramStep :: String -> TapeC TapeP
toProgramStep ('>':cs) = liftI right cs
-- similarly for other instructions
toProgramStep ('[':cs) = push (toProgramStep cs)
toProgramStep (']':cs) = pop (toProgramStep cs)

push :: TapeC TapeP -> TapeC TapeP
push mcontinue = do
  continue <- mcontinue
  cont (\breakMake -> loopControl continue (breakMake continue))

pop :: TapeC TapeP -> TapeC TapeP
pop mbreak = do
  break <- mbreak
  cont (\continueMake -> loopControl (continueMake break) break)

I figured I could somehow use continuations to communicate information from the '[' case to the ']' case and vice-versa, but I don't have a firm enough grasp of Cont to actually do anything besides assemble wild guesses of something that looks like it might work, as seen above with push and pop. This compiles and runs, but the results are garbage.

Can Cont be used to tie the knot appropriately for this situation? If not, then what technique should I use to implement toProgram?


Note 1: I previously had a subtle logic error: loopControl = branch is0 had the Bools reversed.

Note 2: I managed to use MonadFix (as suggested by jberryman) with State to come up with a solution (see the current state of the github repository). I'd still like to know how this could be done with Cont instead.

Note 3: My Racketeer mentor put a similar Racket program together for me (see all revisions). Can his pipe/pipe-out technique be translated into Haskell using Cont?


tl;dr I managed to do this using MonadFix, and someone else managed to do it using Racket's continuation combinators. I'm pretty sure this can be done with Cont in Haskell. Can you show me how?

like image 579
Dan Burton Avatar asked May 12 '12 00:05

Dan Burton


People also ask

What is the process of obtaining future values called?

Process of calculating future value of money from present value is classified as compounding.

Where is continuous compounding used?

Continuous compounding is used to show how much a balance can earn when interest is constantly accruing. This allows investors to calculate how much they expect to receive from an investment earning a continuously compounding rate of interest.

Why is it important to understand present value and future value?

Present value helps the investors in understanding and deciding whether an investment should be made or rejected. Since future value tells about the future gains from an investment, it does not have a significant role in decision making regarding an investment.

What are present and future values?

Present value is the sum of money that must be invested in order to achieve a specific future goal. Future value is the dollar amount that will accrue over time when that sum is invested. The present value is the amount you must invest in order to realize the future value.

How to estimate the fair value of the consideration given in acquisition?

Establish the fair value of the consideration given in the acquisition as given in the below equation: The consideration transferred by the acquirer for the business does not include acquisition -related costs or other amounts transferred in transactions that are not part of the business combination.

How do you calculate the value of an acquisition?

Calculating the value of an acquisition Costs: COGS, operating costs and depreciation assumed to attribute to existing customer revenues on a pro rata basis with new customer revenues.

What is not included in the consideration transferred by the acquirer?

The consideration transferred by the acquirer for the business does not include acquisition -related costs or other amounts transferred in transactions that are not part of the business combination.

How to develop a comprehensive asset valuation model?

Determine the associated valuation methodologies and develop comprehensive asset valuation model depending both on the nature of the asset in question and the availability and reliability of the information available to apply the technique. Calculating the value of an acquisition


Video Answer


2 Answers

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)
like image 66
Sjoerd Visscher Avatar answered Sep 29 '22 12:09

Sjoerd Visscher


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.

like image 38
jberryman Avatar answered Sep 29 '22 12:09

jberryman