Re: [Chicken-users] continuation example: different behavior from other Scheme implementations
Am 19.10.2019 18:57 schrieb felix.winkelm...@bevuta.com: But what I forgot, and which may be the confusing part is that after the mapping is complete, "numbers" (in your example) will be assigned once again - the continuation includes everything following the invocation of "map". Only the REPL "cuts off" this continuation (otherwise you would have an infinite loop, similar to when you would run your example code as a non-interactive program). Thanks again, this makes sense, I had already experienced this infinite loop when using a compiled version of the program. I added a note to the wiki mentioned (https://en.wikibooks.org/wiki/Scheme_Programming/Continuations) telling to be aware of implementation-dependent behavior, just in case someone stumbles on this issue in the future. Cheers, Ricardo ___ Chicken-users mailing list Chicken-users@nongnu.org https://lists.nongnu.org/mailman/listinfo/chicken-users
Re: [Chicken-users] continuation example: different behavior from other Scheme implementations
> I found this in R5RS/R7RS/SRFI-1: "The dynamic order in which proc is > applied to the elements of the lists is unspecified". Indeed redefining > map in all implementations as > > (define (map f l) >(cond ((null? l) '()) > (else (cons (f (car l)) > (map f (cdr l)) > > make all behave the same way (I know this is not a complete map > implementation, is just an example). Exactly. In CHICKEN, "map" is implemented in a (nearly) canonical way (modulo some edge cases for extra arguments, etc.). On interpreted implementations, "map" may be implemented in a lower-level way, perhaps mutating the result list while it is build. Continuations expose such details in a sometimes surprising way. > > R7RS adds this: "If multiple returns occur from map, the values returned > by earlier returns are not mutated". Does this mean returns through > continuation calls? That's correct. > > I was curious how CHICKEN implements map and found the following in > library.scm: > > (define (##sys#map p lst0) >(let loop ((lst lst0)) > (cond ((eq? lst '()) lst) >((pair? lst) > (cons (p (##sys#slot lst 0)) (loop (##sys#slot lst 1))) ) >(else (##sys#error-not-a-proper-list lst0 'map)) ) )) > > So still I don't get why calling any stored continuation appends the > result to the previously computed "numbers", but if the standard allows > this behavior it's not a big deal. As you save the continuation, you effectively retain all local state from executions before the continuation was captured. The result list is constructed recursively, the 2nd return from "p" above will restart the mapping of 6 elements (with element 1 replaced by 76) until the list runs out. What is done before is thus returned once again, with extra elements appended, since you restart the processing of the list, you sort of jump back in time into the recursion, effectively prolonging it. But what I forgot, and which may be the confusing part is that after the mapping is complete, "numbers" (in your example) will be assigned once again - the continuation includes everything following the invocation of "map". Only the REPL "cuts off" this continuation (otherwise you would have an infinite loop, similar to when you would run your example code as a non-interactive program). felix ___ Chicken-users mailing list Chicken-users@nongnu.org https://lists.nongnu.org/mailman/listinfo/chicken-users
Re: [Chicken-users] continuation example: different behavior from other Scheme implementations
Am 19.10.2019 15:46 schrieb megane: There's special compiler syntax for map; search for 'map-loop in the sources. You can also see what your code expands to by giving -debug 2 flag to csc. Thanks megane, with -debug 2 I see that map uses some local variables that are changed with set! in the loop, so it's quite more complex than I thought, and this explains the behavior seen. Thanks for the hints. ;; generated code (set! numbers (let ((g2836 (scheme#cons (##core#undefined) '( (let ((g2737 g2836) (g2938 capture-from-map) (g3539 '(1 2 3 4 5 6))) (let ((t46 (##sys#check-list g3539 'map))) (##core#app (let ((map-loop2340 (##core#undefined))) (let ((t45 (set! map-loop2340 (##core#lambda (g3541) (if (scheme#pair? g3541) (let ((g2442 (scheme#cons (g2938 (##sys#slot g3541 '0)) '( (let ((t43 (##sys#setslot g2836 '1 g2442))) (let ((t44 (set! g2836 g2442))) (##core#app map-loop2340 (##sys#slot g3541 '1) (##sys#slot g2737 '1)) (let () map-loop2340))) g3539) Regards, Ricardo ___ Chicken-users mailing list Chicken-users@nongnu.org https://lists.nongnu.org/mailman/listinfo/chicken-users
Re: [Chicken-users] continuation example: different behavior from other Scheme implementations
Ricardo Gabriel Herdt writes: > Am 19.10.2019 14:18 schrieb felix.winkelm...@bevuta.com: >> The exact behaviour of re- >> entering a continuation captured during execution of "map" is, I think, >> dependent on implementation details (there may be a note about this in >> the >> SRFI-1 document or R7RS, I can't remember right now). > > Many thanks for the fast and detailed answer. > > I found this in R5RS/R7RS/SRFI-1: "The dynamic order in which proc is > applied to the elements of the lists is unspecified". Indeed redefining > map in all implementations as > > (define (map f l) >(cond ((null? l) '()) > (else (cons (f (car l)) > (map f (cdr l)) > > make all behave the same way (I know this is not a complete map > implementation, is just an example). > > R7RS adds this: "If multiple returns occur from map, the values returned > by earlier returns are not mutated". Does this mean returns through > continuation calls? That's how I understand it. > > I was curious how CHICKEN implements map and found the following in > library.scm: > > (define (##sys#map p lst0) >(let loop ((lst lst0)) > (cond ((eq? lst '()) lst) >((pair? lst) > (cons (p (##sys#slot lst 0)) (loop (##sys#slot lst 1))) ) >(else (##sys#error-not-a-proper-list lst0 'map)) ) )) > > ... > (set! scheme#map > (lambda (fn lst1 . lsts) >(if (null? lsts) >(##sys#map fn lst1) >(let loop ((all (cons lst1 lsts))) > (let ((first (##sys#slot all 0))) >(cond ((pair? first) > (cons (apply fn (mapsafe (lambda (x) (car x)) all #t 'map)) > (loop (mapsafe (lambda (x) (cdr x)) all #t 'map)) ) ) > (else (check (##core#inline "C_i_cdr" all) #t 'map) > '() ) ) ) ) ) ) ) > ... > > So still I don't get why calling any stored continuation appends the > result to the previously computed "numbers", but if the standard allows > this behavior it's not a big deal. There's special compiler syntax for map; search for 'map-loop in the sources. You can also see what your code expands to by giving -debug 2 flag to csc. > > Regards, > > Ricardo > > > ___ > Chicken-users mailing list > Chicken-users@nongnu.org > https://lists.nongnu.org/mailman/listinfo/chicken-users ___ Chicken-users mailing list Chicken-users@nongnu.org https://lists.nongnu.org/mailman/listinfo/chicken-users
Re: [Chicken-users] continuation example: different behavior from other Scheme implementations
Am 19.10.2019 14:18 schrieb felix.winkelm...@bevuta.com: The exact behaviour of re- entering a continuation captured during execution of "map" is, I think, dependent on implementation details (there may be a note about this in the SRFI-1 document or R7RS, I can't remember right now). Many thanks for the fast and detailed answer. I found this in R5RS/R7RS/SRFI-1: "The dynamic order in which proc is applied to the elements of the lists is unspecified". Indeed redefining map in all implementations as (define (map f l) (cond ((null? l) '()) (else (cons (f (car l)) (map f (cdr l)) make all behave the same way (I know this is not a complete map implementation, is just an example). R7RS adds this: "If multiple returns occur from map, the values returned by earlier returns are not mutated". Does this mean returns through continuation calls? I was curious how CHICKEN implements map and found the following in library.scm: (define (##sys#map p lst0) (let loop ((lst lst0)) (cond ((eq? lst '()) lst) ((pair? lst) (cons (p (##sys#slot lst 0)) (loop (##sys#slot lst 1))) ) (else (##sys#error-not-a-proper-list lst0 'map)) ) )) ... (set! scheme#map (lambda (fn lst1 . lsts) (if (null? lsts) (##sys#map fn lst1) (let loop ((all (cons lst1 lsts))) (let ((first (##sys#slot all 0))) (cond ((pair? first) (cons (apply fn (mapsafe (lambda (x) (car x)) all #t 'map)) (loop (mapsafe (lambda (x) (cdr x)) all #t 'map)) ) ) (else (check (##core#inline "C_i_cdr" all) #t 'map) '() ) ) ) ) ) ) ) ... So still I don't get why calling any stored continuation appends the result to the previously computed "numbers", but if the standard allows this behavior it's not a big deal. Regards, Ricardo ___ Chicken-users mailing list Chicken-users@nongnu.org https://lists.nongnu.org/mailman/listinfo/chicken-users
Re: [Chicken-users] continuation example: different behavior from other Scheme implementations
> Could someone please explain what's going on? Why would Chicken append > the new list to the old value of 'numbers', which as I understand should > be empty at the moment of the stored continuation? You are re-entering the execution of "map" by calling the stored continuation, which was at that point of time in the process of producing a list, and thus is entered again, producing yet another list of six values (since that is the state of the world captured in the first element of "continuations". The exact behaviour of re- entering a continuation captured during execution of "map" is, I think, dependent on implementation details (there may be a note about this in the SRFI-1 document or R7RS, I can't remember right now). Note also that the "old value of numbers" that you speak of above is the value of a global variable, which will not be part of the continuation. It's the computation that produces the result that you re-enter. I hope this is somewhat understandable, apologies if I failed to make it so. felix ___ Chicken-users mailing list Chicken-users@nongnu.org https://lists.nongnu.org/mailman/listinfo/chicken-users
[Chicken-users] continuation example: different behavior from other Scheme implementations
Hi all, I'm trying to understand how continuations work, and tried out the first example described at "https://en.wikibooks.org/wiki/Scheme_Programming/Continuations; with Chicken (5.1): --- (define continuations '()) (define (push arg) (set! continuations (cons arg continuations))) (define (capture-from-map arg) (call-with-current-continuation (lambda (cc) (push cc) arg))) (define numbers (map capture-from-map '(1 2 3 4 5 6))) ;;REPL numbers (1 2 3 4 5 6) ((car (reverse continuations)) 76) ((result "#") (output . "")) numbers (1 2 3 4 5 6 76 2 3 4 5 6) --- To my surprise, this last value stored at 'numbers' differs from other implementations (and from the example described on the Wiki): Guile/Racket/MIT: (76 2 3 4 5 6) Chez: (1 2 3 4 76 6) Could someone please explain what's going on? Why would Chicken append the new list to the old value of 'numbers', which as I understand should be empty at the moment of the stored continuation? Regards, Ricardo ___ Chicken-users mailing list Chicken-users@nongnu.org https://lists.nongnu.org/mailman/listinfo/chicken-users