[plt-scheme] call/cc puzzle

From: Matthew Flatt (mflatt at cs.utah.edu)
Date: Tue Jan 20 09:58:30 EST 2009

There's no fancy optimization of this example in PLT Scheme. It runs in
unbounded space --- but *very* slowly.

Compare the rate of output when running `grow' versus `stable' in

 (define counter 0)

 (define (noisy-call/cc f)
   (set! counter (+ counter 1))
   (cond
    [(zero? (modulo counter 100))
     (display counter) (newline)])
   (call/cc f))

 (define (grow)
   (let ([rator (call/cc call/cc)])
     (rator
      (call/cc noisy-call/cc))))
  
 (define (stable)
   (let ([rand (call/cc call/cc)])
     ((call/cc noisy-call/cc) rand)))

I've used `let' in both variants to force a particular order of
operation (and also used `cond' instead of `when', etc.) so you can try
`grow' versus `stable' in implementations other than PLT Scheme. I saw
the same qualitative difference between the two across all the
implementations I tried.


At Tue, 20 Jan 2009 14:47:14 +0100, "Jos Koot" wrote:
> Neither did I expect ((call/cc call/cc) (call/cc call/cc)) to run in bound 
> space, but I had it run on my 1.8 Ghz, 1Mb RAM single processor machine for 
> over 15 minutes without noticing any substantial increase of memory usage. 
> That's why I looked for an explanation of space bound behaviour.
> Jos
> 
> ----- Original Message ----- 
> From: "Robby Findler" <robby at eecs.northwestern.edu>
> To: "Jos Koot" <jos.koot at telefonica.net>
> Cc: "Shriram Krishnamurthi" <sk at cs.brown.edu>; <rafkind at cs.utah.edu>; 
> "Scheme PLT" <plt-scheme at list.cs.brown.edu>
> Sent: Monday, January 19, 2009 8:22 PM
> Subject: Re: [plt-scheme] call/cc puzzle
> 
> 
> As far as I am able to tell, when you evaluate left-to-right,
> ((call/cc call/cc) (call/cc call/cc)) should be a non-tail-recursive
> loop (right-to-left requires constant space). Perhaps Matthew has done
> something fancy to mzscheme to be able to optimize something somehow
> to make it tail recursive, or perhaps it is a bug.
> 
> Robby
> 
> On Mon, Jan 19, 2009 at 10:03 AM, Jos Koot <jos.koot at telefonica.net> wrote:
> > My explanation of ((call/cc call/cc) (call/cc call/cc)).
> >
> > Read this in CPS (continuation passing style)
> > For this purpose think in terms of meta-procedures EXEC and APPLY.
> >
> > (EXEC code cont) ; cont=continuation.
> > Executes the code and passes the result to the cont.
> >
> > (APPLY op rand cont) ; op is procedure/operator, rand is argument/operand.
> > Calls the op with the rand for its arg and passes the result to the cont.
> >
> > Notice that (APPLY call/cc A B) --> (APPLY A B B).
> > Write ((call/cc call/cc) (call/cc call/cc)) as ((cc1 cc2) (cc3 cc4)).
> > Let `finish' be the continuation of the top form,
> > for instance the PLRE of a REPL (read-eval-print-loop),
> > but as we shall see the finish is never reached,
> > hence `finish' is irrelevant.
> > Notation: #i=x introduces #i# as short for x.
> >
> > First a WRONG explanation:
> >
> > (EXEC ((cc1 cc2) (cc3 cc4)) finish) =
> > (EXEC (cc1 cc2) #0=(λ (op) (EXEC (cc3 cc4) (λ (rand) (APPLY op rand
> > finish)))))
> > (APPLY cc2 #0# #0#)
> > (APPLY #0# #0# #0#) =
> > (EXEC (cc3 cc4) #1=(λ (rand) (APPLY #0# rand finish)))
> > (APPLY cc4 #1# #1#)
> > (APPLY #1# #1# #1#)
> > (APPLY #0# #1# finish)
> > (EXEC (cc3 cc4) #2=(λ (rand) (APPLY #1# rand finish))) =
> > (APPLY cc4 #2# #2#)
> > (APPLY #2# #2# #2#)
> > (APPLY #1# #2# finish)
> > (APPLY #0# #2# finish)
> > (EXEC (cc3 cc4) #3=(λ (rand) (APPLY #2# finish))) =
> > (APPLY cc4 #3# #3#)
> > (APPLY #2# #3# finish)
> > (APPLY #1# #3# finish)
> > (APPLY #0# #3# finish)
> > (EXEC (cc3 cc4) #4=(λ (rand) (APPLY #3# rand finish))) =
> > (APPLY cc4 #4# #4#)
> > (APPLY #4# #4# #4#)
> > (APPLY #3# #4# finish)
> > (APPLY #2# #4# finish)
> > (APPLY #1# #4# finish)
> > (APPLY #0# #4# finish)
> > (EXEC (cc3 cc4) #5=(λ (rand) (APPLY #4# rand finish))) =
> > etc.
> >
> > This suggests that ((call/cc call/cc) (call/cc call/cc)) would not run in
> > bound space. However, it does, as on my small system can easily be 
> > verified
> > by letting it run for some minutes. Apparently Scheme's designers and
> > implementors ar more clever than shown above. I try again:
> >
> > (EXEC ((cc1 cc2) (cc3 cc4)) finish) =
> > (EXEC (cc1 cc2) #0=(λ (op) (EXEC (cc3 cc4) (λ (rand) (APPLY op rand
> > finish)))))
> > (APPLY cc2 #0# #0#)
> > (APPLY #0# #0# #0#)
> > #999=(EXEC (cc3 cc4) #1=(λ (rand) (APPLY #0# rand finish)))
> > (APPLY cc4 #1# #1#)
> > (APPLY #1# #1# #1#)
> > (APPLY #0# #1# finish)
> > (EXEC (cc3 cc4) #2=(λ (newrand) (APPLY #1# newrand finish))) =
> > (EXEC (cc3 cc4)
> >  (λ (newrand)
> >   (APPLY (λ (rand) (APPLY #0# rand finish)) newrand finish))) =
> > ; Contract the continuation.
> > (EXEC (cc3 cc4) #1=(λ (rand) (APPLY #0# rand finish))) =
> > (EXEC (cc3 cc4) #1#) = #999# ; Space bound loop!
> >
> > Is this a correct interpretation? It strikes me that DrScheme's Debug 
> > shows
> > the WRONG behaviour. That may be due to the fact that debug has to extend
> > each continuation such as to move its arrow and to display results and
> > therefore cannot collaps the continuations. Correct?
> >
> > Jos
> >
> >
> >
> > ----- Original Message -----
> > From: Shriram Krishnamurthi
> > To: rafkind at cs.utah.edu
> > Cc: Scheme PLT
> > Sent: Saturday, January 17, 2009 8:38 PM
> > Subject: Re: [plt-scheme] call/cc puzzle
> >
> > Just remember that putting a use of call/cc in a context changes the 
> > meaning
> > of that use...
> >
> > snip
> >
> > _________________________________________________
> >  For list-related administrative tasks:
> >  http://list.cs.brown.edu/mailman/listinfo/plt-scheme
> >
> > 
> 
> _________________________________________________
>   For list-related administrative tasks:
>   http://list.cs.brown.edu/mailman/listinfo/plt-scheme


Posted on the users mailing list.