I want to break free :-) : break in my code with call/cc does not return a value

i have for (for_next_step.rkt) that allow C style for with break and continue.

Example:

Welcome to DrRacket, version 8.14 [cs].
Language: racket, with debugging; memory limit: 8192 MB.
> (require Scheme+/for_next_step)
> (for ((define i 0) (< i 5) (set! i (+ i 1))) (define x 7) (display i) (newline) (break i))
0
> (for ((define i 0) (< i 5) (set! i (+ i 1))) (define x 7) (display i) (newline) (break))
0
> (for ((define i 0) (< i 5) (set! i (+ i 1))) (define x 7) (display i) (continue) (newline) )
01234
> (for ((define i 0) (< i 5) (set! i (+ i 1))) (define x 7) (display i) (newline) )
0
1
2
3
4
> (define res (for ((define i 0) (< i 5) (set! i (+ i 1))) (define x 7) (display i) (newline) (break i)))
0
result arity mismatch;
 expected number of values not received
  expected: 1
  received: 0
> 

the code of the module is:

(module for_next_step racket

  (provide for
	   ;;for-basic
	   ;;for-next
	   ;;for-basic/break
	   ;;for-basic/break-cont
	   ;;for/break-cont
	   ;;for-each-in
)

  (require (only-in racket/base [for for-racket]) ;; backup original Racket 'for'
	   (for-syntax r6rs/private/base-for-syntax) ; unless :  racket-8.14/share/pkgs/errortrace-lib/errortrace/stacktrace.rkt:709:4: identifier-syntax: undefined; cannot reference an identifier before its definition
	   Scheme+/increment)

(define-syntax for
   (lambda (stx)
     (syntax-case stx ()
       ((kwd (init test incrmt) body ...)
        (with-syntax ((BREAK (datum->syntax (syntax kwd) 'break))
                      (CONTINUE (datum->syntax (syntax kwd) 'continue)))
	  
		     (syntax
		      (call/cc
		       (lambda (escape)
			 (let-syntax ((BREAK (identifier-syntax (escape))))
			   init
			   (let loop ()
			     (when test
			       (call/cc
				(lambda (next)
				  (let-syntax ((CONTINUE (identifier-syntax (next))))
				    (let () ;; allow definitions
				      body ...)))) ; end call/cc
			       incrmt
			       (loop))) ; end let loop
			   ))))) ;; close with-syntax
	))))

) ; end library

increment.rkt:

(module increment racket

  (provide incf
	   add1)
    


;; increment variable
;; this is different than add1 in DrRacket
(define-syntax incf
  (syntax-rules ()
    ((_ x)   (begin (set! x (+ x 1))
		    x))))

(define-syntax add1
  (syntax-rules ()
    ((_ x)   (+ x 1))))

)

i works well

but i would attempt (break i) to return i as break should call the continuation of the current code but it does not return a value.

As this could be interesting to allow break to send info on the moment (index) it break away from the for loop.

Any idea?

with the help of another galaxie... (the Guilaxie :wink: , i mean the Guile scheme community) the solution is:

(define-syntax for
  
  (lambda (stx)
    
    (syntax-case stx ()
      
      ((_ (init test incrmt) body ...)
       
       (with-syntax ((BREAK (datum->syntax stx 'break))
                     (CONTINUE (datum->syntax stx 'continue)))
     (syntax
      (call/cc
       (lambda (escape)
         (let ((BREAK escape))
           init
           (let loop ()
         (when test
           (call/cc
            (lambda (next)
              (let ((CONTINUE next))
            (let () ;; allow definitions
              body ...)))) ; end call/cc
           incrmt
           (loop))) ; end let loop
           ))))) ;; close with-syntax
       ))))

Welcome to DrRacket, version 8.14 [cs].
Language: racket, with debugging; memory limit: 8192 MB.
> (require Scheme+/for_next_step)
> (define res (for ((define i 0) (< i 5) (set! i (+ i 1))) (define x 7) (display i) (newline) (when (= i 2) (break i))))
0
1
2
> (for ((define i 0) (< i 5) (set! i (+ i 1))) (define x 7) (display i) (newline) )
0
1
2
3
4
> (for ((define i 0) (< i 5) (set! i (+ i 1))) (define x 7) (display i) (continue) (newline) )
01234
> res
2

there was no need of kwd , def.rkt or def.scm in Scheme+ works well without it and return and return-rec allow returning a value without problem, this make me find the problem by comparison....

let and let-syntax in the macro should work the same but i have not test it...

note in Racket/R6RS the only working solution is the one with let and let-syntax and all the stuff with kwd and syntax :

(define-syntax for
  
  (lambda (stx)
    
    (syntax-case stx ()
      
      ((kwd (init test incrmt) body ...)
       
       (with-syntax ((BREAK (datum->syntax (syntax kwd) 'break))
                     (CONTINUE (datum->syntax (syntax kwd) 'continue)))
	 (syntax
	  (call/cc
	   (lambda (escape)
             (let ((BREAK escape))
               init
               (let loop ()
		 (when test
		   (call/cc
		    (lambda (next)
		      (let ((CONTINUE next))
			(let () ;; allow definitions
			  body ...)))) ; end call/cc
		   incrmt
		   (loop))) ; end let loop
               ))))) ;; close with-syntax
       ))))

here is a test in a function at REPL in R6RS and SRFI-105 (not used here):

Welcome to DrRacket, version 8.14 [cs].
Language: reader SRFI-105, with debugging; memory limit: 8192 MB.
SRFI-105 Curly Infix parser for Racket Scheme by Damien MATTEI
(based on code from David A. Wheeler and Alan Manuel K. Gloria.)

Possibly skipping some header's lines containing space,tabs,new line,etc  or comments.

Detected R6RS code: #!r6rs

SRFI-105.rkt : number of skipped lines (comments, spaces, directives,...) at header's beginning : 7

Parsed curly infix code result = 

(module aschemeplusr6rsprogram r6rs
(library
 (r6rs-srfi-105-repl)
 (export)
 (import (except (rnrs base (6)) if)
         (only (srfi :43) vector-append)
         (rnrs syntax-case (6))
         (only (racket) print-mpair-curly-braces)
         (only (rnrs control (6)) when)
         (only (rnrs io simple (6)) display newline)
         (Scheme+R6RS))
 (print-mpair-curly-braces #f))

)
> (define (foo) (define res (for ((define i 0) (< i 5) (set! i (+ i 1))) (define x 7) (display i) (newline) (when (= i 2) (break i)))) res)
(define (foo)
  (define res
    (for
     ((define i 0) (< i 5) (set! i (+ i 1)))
     (define x 7)
     (display i)
     (newline)
     (when (= i 2) (break i))))
  res)
#<eof>
> (foo)
(foo)
0
1
2
2
#<eof>
> 

(break i) has well returned 2

This is not directly related to the original question, but if strict R⁶RS compliance is not a requirement, I would use syntax parameters to implement such feature (also available in Guile).

1 Like

i rewrite my code using your links:


(require racket/stxparam)


(define-syntax-parameter break
  (lambda (stx)
    (raise-syntax-error 'break "can only be used inside for")))

(define-syntax-parameter continue
  (lambda (stx)
    (raise-syntax-error 'continue "can only be used inside for")))
  


(define-syntax for
  
  (lambda (stx)
    
    (syntax-case stx ()
      
      ((_ (init test incrmt) body ...)
       
   	 (syntax
	  
	  (call/cc
	   (lambda (escape)
	     ;; In the body we adjust the 'break' keyword so that calls
	     ;; to 'break' are replaced with calls to the escape
	     ;; continuation.
	     (syntax-parameterize
	      ([break (syntax-rules ()
			     [(break vals (... ...))
			      (escape vals (... ...))])])
		 
               init
               (let loop ()
		 (when test
		   (call/cc
		    (lambda (next)
		      ;; In the body we adjust the 'continue' keyword so that calls
		      ;; to 'continue' are replaced with calls to the escape
		      ;; continuation.
		      (syntax-parameterize
		       ([continue (syntax-rules ()
				    [(continue vals (... ...))
				     (next vals (... ...))])])
		 
		       (let () ;; allow definitions
			 body ...)))) ; end call/cc
		   incrmt
		   (loop)))))))))))



and here is some examples of use:

Welcome to DrRacket, version 8.14 [cs].
Language: racket, with debugging; memory limit: 8192 MB.
> (require Scheme+/for_next_step)
> (define res (for ((define i 0) (< i 5) (set! i (+ i 1))) (define x 7) (display i) (newline) (when (= i 2) (break i))))
0
1
2
> (for ((define i 0) (< i 5) (set! i (+ i 1))) (define x 7) (display i) (newline) )
0
1
2
3
4
> (for ((define i 0) (< i 5) (set! i (+ i 1))) (define x 7) (display i) (continue) (newline) )
01234

more readable with SRFI 105 curly infix:

Welcome to DrRacket, version 8.14 [cs].
Language: reader SRFI-105, with debugging; memory limit: 8192 MB.
SRFI-105 Curly Infix parser for Racket Scheme by Damien MATTEI
(based on code from David A. Wheeler and Alan Manuel K. Gloria.)

Possibly skipping some header's lines containing space,tabs,new line,etc  or comments.

SRFI-105.rkt : number of skipped lines (comments, spaces, directives,...) at header's beginning : 8

Parsed curly infix code result = 

(module Racket-SRFI-105-REPL racket (provide (all-defined-out)))
> (require Scheme+/for_next_step)
(require Scheme+/for_next_step)
#<eof>
> (require Scheme+/nfx)
(require Scheme+/nfx)
#<eof>
> (require Scheme+/assignment)
(require Scheme+/assignment)
#<eof>

> (for ((define i 0) {i < 5} {i := i + 1})
    (define x 7)
    (display {x + i})
    (newline)
    (when {i = 2}
      (break i)))

(for
 ((define i 0) (< i 5) ($nfx$ i := i + 1))
 (define x 7)
 (display (+ x i))
 (newline)
 (when (= i 2) (break i)))
$nfx$ : parsed-args=.#<syntax (:= i (+ i 1))>
7
8
9
2
#<eof>
> 

the same works in Guile and only in Scheme that implements stxparams (not Kawa) ,for those the previous solution can be used.

then... i ran my AI code and the neural sine computation show bad results. As the last modification of Scheme+ code was about for/break/continue with stxparams implementation i roll back to the code before stxparams and it worked again.

But for/break/continue seems to work itself in many other codes.

So I tested the Guile version where i used the same implementation of for/break/continue with stxparams and it give good results on the neural sine.

What can we think about that?

i just can stay for the Racket version of Scheme+ to an implementation of for/break/continue that do not use of stxparams.

The code is too big to debug : AI and Scheme+ code is too big, stxparams is not really portable from one scheme to another one, and i will not use it in the future.

I suppose there is some hard problem to understand with the Racket's macro (as it works in Guile) but "macro is hard" and macro which call macros are harder....