Skip to content

Instantly share code, notes, and snippets.

@akeep
Last active December 13, 2015 19:49
Show Gist options
  • Save akeep/4965768 to your computer and use it in GitHub Desktop.
Save akeep/4965768 to your computer and use it in GitHub Desktop.
Trying to unravel @toddaaro's interesting call/cc infite loop
;; So @toddaaro started with the following example (http://hpaste.org/82455)
;;
;; in a scheme script executed via "scheme --script foo.ss"
(let ()
(define foo (map call/cc `(,call/cc ,call/cc)))
(printf "~s ~s\n" 1 (map call/cc foo))
(printf "~s ~s\n" 2 (map call/cc foo))
(printf "~s ~s\n" 3 (map call/cc foo))
(printf "~s ~s\n" 4 (map call/cc foo))
(printf "~s ~s\n" 5 (map call/cc foo)))
;; prints:
1 (#<system continuation in map> #<system continuation in map>)
2 (#<system continuation in map> #<system continuation in map>)
3 (#<system continuation in map> #<system continuation in map>)
4 (#<system continuation in map> #<system continuation in map>)
5 (#<system continuation in map> #<system continuation in map>)
;; swap to begin:
(begin
(define foo (map call/cc `(,call/cc ,call/cc)))
(printf "~s ~s\n" 1 (map call/cc foo))
(printf "~s ~s\n" 2 (map call/cc foo))
(printf "~s ~s\n" 3 (map call/cc foo))
(printf "~s ~s\n" 4 (map call/cc foo))
(printf "~s ~s\n" 5 (map call/cc foo)))
;; prints
1 (#<system continuation in map> #<system continuation in map>)
1 (#<system continuation in map> #<system continuation in map>)
1 (#<system continuation in map> #<system continuation in map>)
1 (#<system continuation in map> #<system continuation in map>)
1 (#<system continuation in map> #<system continuation in map>)
...
(infinite loop!)
;; So, the first step is to simplify the example:
(let ()
(define foo (map call/cc `(,call/cc)))
(printf "~s ~s\n" 1 (map call/cc foo))
(printf "~s ~s\n" 2 (map call/cc foo)))
(begin
(define foo (map call/cc `(,call/cc)))
(printf "~s ~s\n" 1 (map call/cc foo))
(printf "~s ~s\n" 2 (map call/cc foo)))
;; and we still get our infinite loop, but the call/cc in the list is unnecessary for the example so:
(let ()
(define foo (map call/cc `(,(lambda (k) k))))
(printf "~s ~s\n" 1 (map call/cc foo))
(printf "~s ~s\n" 2 (map call/cc foo)))
(begin
(define foo (map call/cc `(,(lambda (k) k))))
(printf "~s ~s\n" 1 (map call/cc foo))
(printf "~s ~s\n" 2 (map call/cc foo)))
;; Still, we get the same behavior. The map too is probably not needed:
(let ()
(define foo (call/cc (lambda (k) k)))
(printf "~s ~s\n" 1 (call/cc foo))
(printf "~s ~s\n" 2 (call/cc foo)))
(begin
(define foo (call/cc (lambda (k) k)))
(printf "~s ~s\n" 1 (call/cc foo))
(printf "~s ~s\n" 2 (call/cc foo)))
;; Still, we get the same behavior, so ofcourse the map is not needed.
;; We can also eliminate the printfs and see that we still have an infinite loop
;; for the begin case and a terminating one for the let case.
;; To see what the difference is, we need to think about the semantics of begin and let.
;; let creates a new scoping level, and more importantly, means that the
;; define is a local define, so it becomes:
(let ()
(letrec* ([foo (call/cc (lambda (k) k))])
(printf "~s ~s\n" 1 (call/cc foo))
(printf "~s ~s\n" 2 (call/cc foo))))
;; Here the first call/cc is not in the middle of an operation so each subsequent
;; call/cc causes the foo to jump to the next call/cc
;; begin on the other hand, is a splicing form, so the define is a top-level define.
;; In Chez Scheme, this means it is a top-level-set!:
(begin
(set! foo (call/cc (lambda (k) k)))
(printf "~s ~s\n" 1 (call/cc foo))
(printf "~s ~s\n" 2 (call/cc foo)))
;; Here, however, we end up jumping back into the set!, and effectively
;; reset the call/cc to the first call/cc, each time, so we do the first
;; printf and then repeat.
;; Interestingly, in Racket, it prints the first message in the begin,
;; but not the second. It then terminates after that.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment