[plt-scheme] Re: Continuations memory accumulation problem

From: Ciprian Dorin, Craciun (ciprian.craciun at gmail.com)
Date: Mon Mar 15 14:54:38 EDT 2010

On Mon, Mar 15, 2010 at 8:28 PM, Anthony Cowley <acowley at seas.upenn.edu> wrote:
> On Mon, Mar 15, 2010 at 8:08 AM, Ciprian Dorin, Craciun
> <ciprian.craciun at gmail.com> wrote:
>>    Could someone explain this to me?
> I'm going to give an answer that will probably only be partially
> helpful. First, are you really committed to doing things this way? I
> would certainly recommend a more functional approach to computing new
> robot states from old ones. The continuation approach is confusing,
> and ends up involving you passing around robot state in any case, it's
> just that robot state is a per-robot continuation holding on to
> references to who-knows-what (which can lead to memory leaks). Bear in
> mind that, ideally, you have a continuation per-robot and a
> continuation for the scheduler. If the per-robot continuations close
> over other continuations, in particular continuations of the larger
> program state that might involve running other robots, then things can
> go sideways.

    First thanks for your reply! I'll try your code tomorrow morning.

    About my commitment for the continuation approach: indeed from a
usage point of view it's not such a good approach. But unfortunately
I'm forced to do it this way. In what follows I'll show you how a
robot control function looks like (what should have been actually
placed inside `do-robot!`):

#lang scheme
(require "robots.ss")

(define (control-robot! robot)
	(when (feel-robot? robot 'baddie)
		(zap-robot! robot))
	(when (look-robot? robot '(food prize))
		(move-robot! robot)
		(grab-robot! robot))
	(unless (look-robot? robot '(food prize))
		(turn-robot! robot (- (random 3) 1)))
	(unless (look-robot? robot '(food prize))
		(move-robot! robot (+ (random 2) 1)))
	(control-robot! robot))

(simulate-robot control-robot!)

    As you see in this user code there is no mention of those
continuations. It's just a plain simple tail-recursive function that
controls the robot with a simple AI.

    So where am I using the continuations? Well inside the
`simulate-robot` function. What happens inside is the following:
    * I create a graphical interface (frame%, canvas%, etc.), and a
timer% that governs the speed of my simulation; the timer is actually
the heartbeat of every robot; each time a timeout elapses I need to
execute one and only one robot action;
    * so looking inside `turn-robot!` I'm updating of the robot-state,
updating the bitmap to be displayed, and I return control to the
graphical framework event loop;
    * next-time when the timer raises a notice event I need to
continue from inside the `turn-robot!` function, return to the
`control-robot!` function and continue with the user AI code;
    * all this jumping inside and outside of actions is accomplished
by using the continuations I've mentioned;

    If there is another better way to do it (without using threads or
continuations, but also keeping the AI code simple) please let me

> I'm not completely certain this does what you want, but this is a
> version using delimited continuations that tries to be faithful to
> your overall design.
> I think it's quite a bit less code, and the robot behavior
> specification is pretty well isolated from control structures. Each
> time around, the robot picks up where it left off, does its work, then
> stashes away a bookmark to its current state and kicks back out to the
> scheduler via calls to control. The main point, though, is to simplify
> the code to make debugging easier.
> Anthony

    So thanks again for the insight. I'll try the code tomorrow.


    P.S.: Is there any good (as in more detailed) documentation about
continuations (and their particularities in PLT Scheme)? (The Guide
doesn't enter in all the details, and the Reference is full of little
puzzle pieces that it's hard to see how they fit together.)

    P.P.S.: It seems that the `prompt` structure is similar with the
`call-with-continuation-prompt` combined with
`call-with-current-continuation`, and `control` is similar with
exiting the continuation to the previous prompt (like an abort). I'll
have to play with these control structures...

> ---------------------------
> #lang scheme
> (require scheme/control)
> (define-struct robot
>  (continuation) #:mutable)
> (define (create-robot)
>  (make-robot (void)))
> (define (step-robots! robots)
>  (for ((robot (in-list robots)))
>       (cond
>         ((void? (robot-continuation robot))
>          (start-robot! robot))
>         ((continuation? (robot-continuation robot))
>          (enter-robot! robot))
>         (else (printf "died~n"))))
>  (unless (andmap (compose not robot-continuation) robots)
>    (step-robots! robots)))
> (define (start-robot! robot)
>  (prompt
>   (printf "Starting robot~n")
>   (do-robot! robot)
>   (done-robot! robot)))
> (define (enter-robot! robot)
>  (prompt ((robot-continuation robot))))
> (define (exit-robot! robot)
>  (control k (set-robot-continuation! robot k) #t))
> (define (done-robot! robot)
>  (printf "Done!~n")
>  (set-robot-continuation! robot #f))
> (define (do-robot! robot (steps 100000))
>  (unless (zero? steps)
>    (exit-robot! robot)
>    (do-robot! robot (- steps 1))))
> (define robots (list (create-robot) (create-robot) (create-robot)))
> (define (go) (step-robots! robots))

Posted on the users mailing list.