Macro does not works in R6RS but in Racket

hello,

the code below used to works in Racket but not R6RS:

#!r6rs

(library (def)


  (export 
	  if-defined)
  
  (import (rnrs base (6))
	  (only (racket) identifier-binding))

;;(define-syntax (if-defined stx)
  (define-syntax if-defined
    (lambda (stx)
      (syntax-case stx ()
	[(_ id iftrue iffalse)
	 (let ([exist-id (identifier-binding #'id)])
	   ;;(display "id=") (display #'id) (newline)
	   ;;(display "if-defined : exist-id=") (display exist-id) (newline) (newline)
	   (if exist-id #'iftrue #'iffalse))])))

  ) ; end library

why?

i do not understand the error:

Welcome to DrRacket, version 8.13 [cs].
Language: r6rs, with debugging; memory limit: 8192 MB.
. lambda: unbound identifier;
 also, no #%app syntax transformer is bound in the transformer phase in: lambda

Interactions disabled: r6rs does not support a REPL (no #%top-interaction)

regards,

damien

Probably because racket is not required for-syntax (for phase 1 as well as for phase 0).

You get a similar error message for this program, which uses #lang racket/base instead of #lang racket:

#lang racket/base
(define-syntax if-defined
  (lambda (stx)
    (syntax-case stx ()
      [(_ id iftrue iffalse)
       (let ([exist-id (identifier-binding #'id)])
         ;;(display "id=") (display #'id) (newline)
         ;;(display "if-defined : exist-id=") (display exist-id) (newline) (newline)
         (if exist-id #'iftrue #'iffalse))])))

The usual fix is to add

(require (for-syntax racket/base))

(Which, in effect, #lang racket already does for you. Whereas #lang racket/base is supposed to be much more minimal.)

You know r6rs better than me, so maybe you already know how it would let you do an equivalent "for syntax" import?

1 Like

This is the working program:

#!r6rs

(library (def)
  (export if-defined)
  (import (rnrs base)
          (for (rnrs base) expand)
          (for (rnrs syntax-case) expand)
          (for (only (racket) identifier-binding) expand))

  (define-syntax if-defined
    (lambda (stx)
      (syntax-case stx ()
        [(_ id iftrue iffalse)
         (let ([exist-id (identifier-binding #'id)])
           (if exist-id #'iftrue #'iffalse))]))))

Explanation: Racket implements what is called “explicit phasing”, which requires you to specify precisely what to import at which phase. Most other R⁶RS implementations instead adopt a strategy called “implicit phasing”, effectively making each export available at all phases. This is a caveat portable R⁶RS programs must account for.

3 Likes

yes the original code working racket code was using for-syntax. (i'm porting code from Racket to R6RS modules,also code from Kawa and Guile modules to Racket/R6RS)

all the 2 answers works, strangely can not find the doc for (import (for clause and adding or not (6) that should be RnRS version does not matter.

Thanks

The reference on R⁶RS is just, well, R⁶RS itself. You should read that to find language specification. For example, library forms are specified in Chapter 7.

yes i did not noticed it in R6RS. Also as porting code from Kawa/R7RS but i can't find the (import (for ... feature anymore in R7RS doc.
Did they really remove it or again i missed it?

ref. : https://standards.scheme.org/official/r7rs.pdf

The phase system (or any kind of serious macro system) is nonexistent in R⁷RS-small; not sure about R⁷RS-large, but I think they favor implicit phasing, and existing R⁷RS implementations likely adopt that.

1 Like

ok.

now in R6RS with Racket i have this part of code that no more works, i'm not sure it is the same probleme, seems not, and this was workin in Racket,Guile/R6RS,Kawa/R7RS:

#!r6rs

(library (def)


  (export def
	        if-defined)
  
  (import (rnrs base (6)) ;; i added version (6) but original discourse answer was without
	  (rnrs syntax-case (6))
	  (for (rnrs base (6)) expand) ; import at expand phase (not run phase)
          (for (rnrs syntax-case (6)) expand)
          (for (only (racket) identifier-binding) expand))


(define-syntax if-defined
    (lambda (stx)
      (syntax-case stx ()
	[(_ id iftrue iffalse)
	 (let ([exist-id (identifier-binding #'id)])
	   ;;(display "id=") (display #'id) (newline)
	   ;;(display "if-defined : exist-id=") (display exist-id) (newline) (newline)
	   (if exist-id #'iftrue #'iffalse))])))


;; scheme@(guile-user)> (def (foo) (when #t (return "hello") "bye"))
;; scheme@(guile-user)> (foo)
;;  "hello"

;; (def x)

(define-syntax def

  (lambda (stx)
    
      (syntax-case stx ()

	;; multiple definitions without values assigned
	;; (def (x y z))
	;; TODO: remove? redundant with (declare x y z)
	((_ (var1 ...)) #`(begin (define var1 '()) ...))
	
	;;  (def (foo) (when #t (return "hello") "bye"))
        ;; ((_ (<name> <arg> ...) <body> <body>* ...)
        ;;  (let ((ret-id (datum->syntax stx 'return)))
        ;;    #`(define (<name> <arg> ...)
        ;;        (call/cc (lambda (#,ret-id) <body> <body>* ...)))))


	((_ (<name> <arg> ...) <body> <body>* ...)
	 
         (let ((ret-id (datum->syntax stx 'return))
	       (ret-rec-id (datum->syntax stx 'return-rec)))

	   #`(define (<name> <arg> ...)

	       (call/cc (lambda (#,ret-rec-id)
			  
			 (apply (rec <name> (lambda (<arg> ...)
					      (call/cc (lambda (#,ret-id) <body> <body>* ...)))) (list <arg> ...)))))))

	      

	;; single definition without a value assigned
	;; (def x)
	((_ var) #`(define var '()))

	;; (def x 7)
	((_ var expr) #`(define var expr))

	((_ err ...) #`(syntax-error "Bad def form"))

	)))

) ; end library

the problem is with 'def' ,not 'if-defined' which is not used in this file,
i got this error when running the test example:
the goal is to allow 'return' and 'return-rec' in a 'define'-ed procedure, this used to works still many years in Racket/Guile/Kawa....

Welcome to DrRacket, version 8.13 [cs].
Language: r6rs, with debugging; memory limit: 8192 MB.
> datum->syntax
#<procedure:r6rs:datum->syntax>
> (def (foo) (when #t (return "hello") "bye"))
. . datum->syntax: expected argument of type <identifier?>; given: #<syntax:46-interactions from an unsaved editor:5:2 (def (foo) (when #t (return "hello") "bye"))>
> when
. . when: undefined;
 cannot reference an identifier before its definition
> (def (bar) '())
. . datum->syntax: expected argument of type <identifier?>; given: #<syntax:46-interactions from an unsaved editor:10:2 (def (bar) (quote ()))>
> def
. def: bad syntax in: def

seems datum->syntax act differently in R6RS/Racket than before in Racket and no more accept my arguments.

The error says the first argument of (R^RS) datum->syntax must be an identifier.
You gave it a syntax object with (def (foo) ...).

The R6RS docs:

(datum->syntax template-id datum)‌‌ procedure

Template-id must be a template identifier and datum should be a datum value. The datum->syntax procedure returns a syntax-object representation of datum that contains the same contextual information as template-id, with the effect that the syntax object behaves as if it were introduced into the code when template-id was introduced.

i had read the doc. Code worked without modification in Racket, Guile but without R6RS because the R6RS implementation of Guile has some problems,Kawa with R7RS support from kawa. Now when i modify it by changing stx in (syntax stx) there is no more error but it fails later:

Welcome to DrRacket, version 8.13 [cs].
Language: r6rs, with debugging; memory limit: 8192 MB.
> (def (foo) (when #t (return "hello") "bye"))
def.scm : def : ret-id = .#<syntax return>
def.scm : def : ret-rec-id = .#<syntax return-rec>
> (foo)
. . return: undefined;
 cannot reference an identifier before its definition

the code now is this:

;;  /Applications/Racket\ v8.13/bin/plt-r6rs --install def.scm 


#!r6rs

(library (def)


  (export def
	  if-defined)
  
  (import (rnrs base (6)) ;; i added version (6) but original discourse answer was without
	  (rnrs syntax-case (6))
	  (only (srfi :31) rec)
	  (only (rnrs control (6)) when)
	  (for (rnrs base (6)) expand) ; import at expand phase (not run phase)
          (for (rnrs syntax-case (6)) expand)
          (for (only (racket) identifier-binding) expand)
	  (for (only (rnrs io simple (6)) display newline) expand))
	  

; Tests
;; (define x 3)
;; (if (defined? x) 'defined 'not-defined) ; -> defined

;; (let ([y 4])
;;    (if (defined? y) 'defined 'not-defined)) ; -> defined

;; (if (defined? z) 'defined 'not-defined) ; -> not-defined
;; (define-syntax (defined? stx)
;;   (syntax-case stx ()
;;     [(_ id)
;;      (with-syntax ([v (identifier-binding #'id)]) ; Racket feature , not RnRS
;;        #''v)]))

; Tests
;; (if-defined z (list z) 'not-defined) ; -> not-defined

;; (if-defined t (void) (define t 5))
;; t ; -> 5

;; (define x 3)
;; (if-defined x (void) (define x 6))
;; x ; -> 3

;;(define-syntax (if-defined stx)
(define-syntax if-defined
    (lambda (stx)
      (syntax-case stx ()
	[(_ id iftrue iffalse)
	 (let ([exist-id (identifier-binding #'id)])
	   ;;(display "id=") (display #'id) (newline)
	   ;;(display "if-defined : exist-id=") (display exist-id) (newline) (newline)
	   (if exist-id #'iftrue #'iffalse))])))


;; scheme@(guile-user)> (def (foo) (when #t (return "hello") "bye"))
;; scheme@(guile-user)> (foo)
;;  "hello"

;; (def x)

;;(define return '()) ;; for debug of Typed Racket

(define-syntax def

  (lambda (stx)
    
      (syntax-case stx ()

	;; multiple definitions without values assigned
	;; (def (x y z))
	;; TODO: remove? redundant with (declare x y z)
	((_ (var1 ...)) #`(begin (define var1 '()) ...))
	
	;;  (def (foo) (when #t (return "hello") "bye"))
        ;; ((_ (<name> <arg> ...) <body> <body>* ...)
        ;;  (let ((ret-id (datum->syntax stx 'return)))
        ;;    #`(define (<name> <arg> ...)
        ;;        (call/cc (lambda (#,ret-id) <body> <body>* ...)))))


	((_ (<name> <arg> ...) <body> <body>* ...)
	 
         (with-syntax ((ret-id (datum->syntax (syntax stx) 'return))
		       (ret-rec-id (datum->syntax (syntax stx) 'return-rec)))

		      (display "def.scm : def : ret-id = ") (display #'ret-id) (newline)
		      (display "def.scm : def : ret-rec-id = ") (display #'ret-rec-id) (newline)

	   #'(define (<name> <arg> ...)

	       (call/cc (lambda (ret-rec-id) ;(#,ret-rec-id)
			  
			 (apply (rec <name> (lambda (<arg> ...)
					      (call/cc (lambda (ret-id) ;(#,ret-id)
							 <body> <body>* ...))))
				(list <arg> ...)))))))

	      

	;; single definition without a value assigned
	;; (def x)
	((_ var) #`(define var '()))

	;; (def x 7)
	((_ var expr) #`(define var expr))

	((_ err ...) #`(syntax-error "Bad def form"))

	)))

) ; end library

i try to port my code to R6RS or R7RS to make it more easy to port to other scheme implementation but perheaps this special procedure can not be done in R6RS. Kawa and Guile are not completely R6RS compatible so there is some mix scheme/R6RS and perheaps RAcket/R6RS is good and reject this code.

The identifier stx appears in your code and has the context of the macro definition.
I think, your intention was to use the context of the macro call.

The macro call is (def ...) so you can use the (car (syntax-e stx)) to get the def identifier.

Note:

The #lang r6rs implementation is a strict implementation of the R6RS specification.
That means, that it implements R6RS without any extensions. The plus side is that a program that runs in #lang r6rs will run on all other R6RS implementions.

Most R6RS implementations are more lax and implement extenstions to the specification.
One such example is allowing the first argument of datum->syntax to be a non-identifier.
Arguing that a program runs in Kawa or Guile doesn't imply that the program in question follows the R6RS specification.

Compare the situation to the identifier problem from the other day. Most implementations allow more identifiers than a strict reading of the specification does.

i did not wrote the code myself ,someone else do it. I just modify it to allow return-rec (that return from all recursive calls)

what do you mean by 'syntax-e' ? i suppose there is a typo but i do not know how to correct it.

yes about kawa and guile, i have been warned to do not use guile/r6rs so i use guile module system ,about kawa i'm not even know if there is a support for r6rs , only it worked with a r7rs-like syntax in kawa.Sorry for the confusion in the previous post about that.

The Racket function syntax-e unpacks a syntax object.
The function syntax->datum repeatedly calls syntax-e.
Checking R6RS I see it has no syntax-e.
So use pattern matching to get the def identifier instead.

just for info ,in Racket the program perform nice:

(module def racket


  (provide def
	   if-defined)

  (require srfi/31) ;; for 'rec in def.scm
  
	  

; Tests
;; (define x 3)
;; (if (defined? x) 'defined 'not-defined) ; -> defined

;; (let ([y 4])
;;    (if (defined? y) 'defined 'not-defined)) ; -> defined

;; (if (defined? z) 'defined 'not-defined) ; -> not-defined
;; (define-syntax (defined? stx)
;;   (syntax-case stx ()
;;     [(_ id)
;;      (with-syntax ([v (identifier-binding #'id)]) ; Racket feature , not RnRS
;;        #''v)]))

; Tests
;; (if-defined z (list z) 'not-defined) ; -> not-defined

;; (if-defined t (void) (define t 5))
;; t ; -> 5

;; (define x 3)
;; (if-defined x (void) (define x 6))
;; x ; -> 3

;;(define-syntax (if-defined stx)
(define-syntax if-defined
    (lambda (stx)
      (syntax-case stx ()
	[(_ id iftrue iffalse)
	 (let ([exist-id (identifier-binding #'id)])
	   ;;(display "id=") (display #'id) (newline)
	   ;;(display "if-defined : exist-id=") (display exist-id) (newline) (newline)
	   (if exist-id #'iftrue #'iffalse))])))


;; scheme@(guile-user)> (def (foo) (when #t (return "hello") "bye"))
;; scheme@(guile-user)> (foo)
;;  "hello"

;; (def x)

;;(define return '()) ;; for debug of Typed Racket

(define-syntax def

  (lambda (stx)
    
      (syntax-case stx ()

	;; multiple definitions without values assigned
	;; (def (x y z))
	;; TODO: remove? redundant with (declare x y z)
	((_ (var1 ...)) #`(begin (define var1 '()) ...))
	
	;;  (def (foo) (when #t (return "hello") "bye"))
        ;; ((_ (<name> <arg> ...) <body> <body>* ...)
        ;;  (let ((ret-id (datum->syntax stx 'return)))
        ;;    #`(define (<name> <arg> ...)
        ;;        (call/cc (lambda (#,ret-id) <body> <body>* ...)))))


	((_ (<name> <arg> ...) <body> <body>* ...)
	 
         (with-syntax ((ret-id (datum->syntax stx 'return))
		       (ret-rec-id (datum->syntax stx 'return-rec)))

		      (display "def.scm : def : ret-id = ") (display #'ret-id) (newline)
		      (display "def.scm : def : ret-rec-id = ") (display #'ret-rec-id) (newline)

	   #'(define (<name> <arg> ...)

	       (call/cc (lambda (ret-rec-id) ;(#,ret-rec-id)
			  
			 (apply (rec <name> (lambda (<arg> ...)
					      (call/cc (lambda (ret-id) ;(#,ret-id)
							 <body> <body>* ...))))
				(list <arg> ...)))))))

	      

	;; single definition without a value assigned
	;; (def x)
	((_ var) #`(define var '()))

	;; (def x 7)
	((_ var expr) #`(define var expr))

	((_ err ...) #`(syntax-error "Bad def form"))

	)))
Welcome to DrRacket, version 8.13 [cs].
Language: r6rs, with debugging; memory limit: 8192 MB.
> (def (foo) (when #t (return "hello") "bye"))
def.scm : def : ret-id = .#<syntax return>
def.scm : def : ret-rec-id = .#<syntax return-rec>
> (foo)
"hello"

the procedure foo has well escaped with return and return value "hello" without executing "bye".

I did not see why in R6RS it make so much difference in datum->syntax ?

i could try importing datum->syntax from Racket, should not be possible in R6RS , this is a whole system, i understand the notion syntax is different in R6RS than Racket (unless internally there is some equivalence....)

Other solution keep this Racket module def and use it in R6RS code, as a module perheaps it should have more chance to coexist...

Third solution ,drop support for R6RS , this the partial way i do it in Guile (bug in Guile R6RS) and Kawa (mox R7RS/Kawa) anyway the code performed well in Racket/Guile/Kawa. I just modularize it in Guile and Kawa and tried to make it R6RS the module way for portability.

Well if i make it modularized in Racket this is enought for me to be satisfied.

it seems i have been able to make it run by mixing R6RS and Racket at least in an R6RS module:

#!r6rs
(library (r6repl)

  (export)

  (import
      (rnrs base (6))
      ;(rnrs syntax-case (6))
      (only (racket) print-mpair-curly-braces require)
      (only (rnrs control (6)) when)
      ;;(def)
      )
     

  (require "def.scm")
  (print-mpair-curly-braces #f) 

)

the R6RS module accepted to 'require' the def.scm non-R6RS racket module in the same directory.

Here is the code in action:

Welcome to DrRacket, version 8.13 [cs].
Language: r6rs, with debugging; memory limit: 8192 MB.
> (def (foo) (when #t (return "hello") "bye"))
def.scm : def : ret-id = .#<syntax return>
def.scm : def : ret-rec-id = .#<syntax return-rec>
> (foo)
"hello"

notice it is here in r6rs language

If there is no side effect i will continue porting the code to R6RS and mixing non-compatible procedures written in Racket. I think this one was the hardest one ,there should not be too much others.

better way is to install the Racket module def with the GUI install package or in command line and import it in the R6RS code, then the R6RS code can be installed with plt-r6rs:

main.rkt in def directory:

#lang reprovide
"def.scm"

;; install reprovide lang package !

in GUI:

#!r6rs
(library (r6repl)

  (export)

  (import
      (rnrs base (6))
      ;(rnrs syntax-case (6))
      (only (racket) print-mpair-curly-braces );require)
      (only (rnrs control (6)) when)
      (def)
      ;(operators-list)
      ;(infix)
      ;(infix-with-precedence-to-prefix)
      ;(parse-square-brackets)
      )

  ;(require def) ;;"def/def.scm") ;; def) ; "def.scm")
  (print-mpair-curly-braces #f) 

)

R6RS code to install as a r6rs package:

for example:

#!r6rs


(library (parse-square-brackets) ; R6RS

  (export parse-square-brackets-arguments-lister-syntax)
  
  (import
   (rnrs base (6))
   (only (rnrs control (6)) when)
   ;(only (racket) require)
   (def)
   (declare)
   (block)
   (syntax)
   (slice)
   (infix-with-precedence-to-prefix)
   (infix)
   (operators)
   (operators-list))
	  

 ;; (require def) ;;"def.scm")

  
;; split the expression using slice as separator
(def (parse-square-brackets-arguments args-brackets creator operator-precedence)

  ;;(display "parse-square-brackets-arguments : args-brackets=") (display args-brackets) (newline)

  (define operators-lst (apply append operator-precedence))
  
  (when (null? args-brackets)
	(return args-brackets))

  (declare result partial-result)
  
  (def (psba args) ;; parse square brackets arguments ,note: it is a tail-recursive function (see end)

       ;;(display "psba : args=") (display args) (newline)
       ;;(display "psba : partial-result =") (display partial-result) (newline)
       (when (null? args)
  	     ;;(display "before !*prec") (newline)
  	     (if (infix?  partial-result operators-lst)
  		 (set! result (append result (!*prec-generic partial-result  operator-precedence creator))) ;; !*prec-generic is defined in optimize-infix.scm
  		 (set! result (append result partial-result)))
  	     ;; (display "after !*prec") (newline)
  	     ;; (display result) (newline)
  	     ;; (display "return-rec") (newline)
  	     (return-rec result)) ;; return from all recursive calls, as it is tail recursive
       
       (define fst (car args))

       ;;(display "fst=") (display fst) (newline)

       ;; test here for ',' for multi-dim arrays , that will remove the use of { } in [ ]
       (if (datum=? slice fst) ; separator
	      
  	   ($>
  	    ;;(display "slice detected") (newline)
  	    ;;(display "psba : partial-result =") (display partial-result) (newline)
  	    (when (not (null? partial-result))
  		  ;;(display "not null") (newline)
  		  (if (infix?  partial-result operators-lst) ;;  operateurs quotés ou syntaxés !
  		      (begin
  			;;(display "infix detected") (newline)
  			(set! result (append result (!*prec-generic partial-result  operator-precedence creator)))) ;; convert to prefix and store the expression
  		      (set! result (append result partial-result))) ; already atom
  		  (set! partial-result  '())) ;; empty for the next possible portion between slice operator
  	    (set! result  (append result (list fst)))) ;; append the slice operator
	   
  	   (set! partial-result (append partial-result (list fst)))) ;; not a slice operator but append it

       ;;(display "psba : result=") (display result) (newline)
       ;;(display "psba 2 : partial-result=") (display partial-result) (newline)
       
       (psba (cdr args))) ;; end def, recurse (tail recursive)


  ;;(display "parse-square-brackets-arguments : args-brackets=") (display args-brackets) (newline)
  (define rs  (psba args-brackets))
  ;;(display "parse-square-brackets-arguments : rs=") (display rs) (newline)
  rs
  ) ;; initial call



;; (define (parse-square-brackets-arguments-lister args-brackets)
;;   ;;(display "parse-square-brackets-arguments-lister : args-brackets=") (display args-brackets) (newline)
;;   (parse-square-brackets-arguments args-brackets
;; 					     (lambda (op a b) (list op a b))
;; 					     infix-operators-lst-for-parser))


(define (parse-square-brackets-arguments-lister-syntax args-brackets)
  ;;(newline) (display "parse-square-brackets-arguments-lister-syntax : args-brackets=") (display args-brackets) (newline)
  (parse-square-brackets-arguments args-brackets ;; generic procedure
				   (lambda (op a b) (list op a b))
				   ;;infix-operators-lst-for-parser-syntax ;; defined elsewhere
				   (get-infix-operators-lst-for-parser-syntax)))
					    

;; DEPRECATED
;; (define (parse-square-brackets-arguments-evaluator args-brackets)
;;   ;;(display "parse-square-brackets-arguments-evaluator : args-brackets=") (display args-brackets) (newline)
;;   (parse-square-brackets-arguments args-brackets
;; 					     (lambda (op a b) (op a b))
;; 					     (get-operator-precedence)))

'()

) ; end module
code here

then the install success:

/Applications/Racket\ v8.13/bin/plt-r6rs --install --force parse-square-brackets.scm 
 [installing /Users/mattei/Library/Racket/8.13/collects/parse-square-brackets/main.ss]
 [Compiling /Users/mattei/Library/Racket/8.13/collects/parse-square-brackets/main.ss]
def.scm : def : ret-id = #<syntax return>
def.scm : def : ret-rec-id = #<syntax return-rec>
def.scm : def : ret-id = #<syntax return>
def.scm : def : ret-rec-id = #<syntax return-rec>



then there is no need of Racket require, just R6RS code (unless def of course)

note it had been hard to do and install, at some point the GUI was unable to really remove a bad def module and i had to do it by force in cli:

rm -rf ../../../../Library/Racket/8.13/collects/def

it could have be better to make all in cli with raco