Last active
December 13, 2015 19:49
-
-
Save akeep/4965768 to your computer and use it in GitHub Desktop.
Trying to unravel @toddaaro's interesting call/cc infite loop
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;; 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