Macro calling macro gives "undefined variable" in Gambit Scheme - macros

In Gambit Scheme, I can't seem to invoke a macro in the definition of another macro if I compile the file. Here is a contrived example:
;;;; example.scm
(define-macro (w/gensyms gs body)
`(let ,(map (lambda (g) `(,g (gensym ',g)))
gs)
,body))
(define-macro (compose-macro f g)
(w/gensyms (x)
`(lambda (,x) (,f (,g ,x)))))
(define my-cadr
(lambda (x)
((compose-macro car cdr) x)))
;; $ gsc example.scm
;; *** ERROR IN #<procedure #2> -- Unbound variable: w/gensyms
However, if I load the file with the (include ...) special form in the interpreter, it works
$ gsi
> (include "example.scm")
> (pp my-cadr)
(lambda (x) ((lambda (#:x0) (car (cdr #:x0))) x))
Does anyone know what is going on here? Can I convince Gambit to let me use w/gensyms in the definition of another macro in a compiled file?

This is most likely related to phases.
Try this:
Put w/gensyms in a file a.scm and put compose-macro in a file b.scm that imports a.scm.

This is a phasing problem. You want the definition of w/gensyms to be available in the body of subsequent macros. This can be achieved with a for-syntax macro that forces the evaluation of the macro definition at syntax expansion time:
(define-macro (for-syntax . body)
(eval `(begin ,#body))
`(begin))
(for-syntax
(define-macro (w/gensyms gs body)
`(let ,(map (lambda (g) `(,g (gensym ',g)))
gs)
,body)))
If you want the macro to be available both from within other macro definitions and within non-macro definition code you can use this instead:
(define-macro (for-syntax . body)
(eval `(begin ,#body))
`(begin ,#body))
For this specific example, since you are using the macro at a single place, you could have done this:
(define-macro (compose-macro f g)
(define-macro (w/gensyms gs body)
`(let ,(map (lambda (g) `(,g (gensym ',g)))
gs)
,body))
(w/gensyms (x)
`(lambda (,x) (,f (,g ,x)))))
A related approach to address phasing issues is to put the definition of w/gensyms and other macros in the file "macros.scm" and do:
(define-macro (compose-macro f g)
(include "macros.scm")
(w/gensyms (x)
`(lambda (,x) (,f (,g ,x)))))

Related

How to expand a macro in MIT Scheme

I have written a simple macro:
(define-syntax myif
(syntax-rules ()
((_ condition a b)
(if condition a b))))
Usage example: (myif #t "yes" "no").
In MIT Scheme, how do I show the macro expansion of the example above? Is there something similar to Common Lisp's macroexpand and macroexpand-1 or Racket's expand and expand-once?
(MIT Scheme version: 11.2)
% cat macro.scm
(define-syntax myif
(syntax-rules ()
((_ condition a b)
(if condition a b))))
% mit-scheme --silent
(sf "macro.scm")
;Generating SCode for file: "macro.scm" => "macro.bin"...
; This program does not have a USUAL-INTEGRATIONS declaration.
; Without this declaration, the compiler will be unable to perform
; many optimizations, and as a result the compiled program will be
; slower and perhaps larger than it could be. Please read the MIT
; Scheme User's Guide for more information about USUAL-INTEGRATIONS.
;... done
(pp (fasload "macro.bin"))
;Loading "macro.bin"... done
(define-syntax myif
(er-macro-transformer
(lambda (form rename compare)
(if (and (pair? form)
(let ((temp (cdr form)))
(and (pair? temp)
(let ((temp (cdr temp)))
(and (pair? temp)
(let ((temp (cdr temp)))
(and (pair? temp)
(null? (cdr temp)))))))))
(list (rename 'if)
(car (cdr form))
(car (cdr (cdr form)))
(car (cdr (cdr (cdr form)))))
(ill-formed-syntax form)))))
will print the Scode. This is enough for debugging.
As it's expressed in the warning of compilation, it is important not to activate the optimizations, otherwise you won't see any more the literal translation into Scode.
This is the starting point when I debug (not only macros).

How can macro variable capture happen with a gensym symbol?

I'm learning common lisp. I have written a version of the once-only macro, which suffers from an unusual variable capture problem.
My macro is this:
(defmacro my-once-only (names &body body)
(let ((syms (mapcar #'(lambda (x) (gensym))
names)))
``(let (,,#(mapcar #'(lambda (sym name) ``(,',sym ,,name))
syms names))
,(let (,#(mapcar #'(lambda (name sym) `(,name ',sym))
names syms))
,#body))))
The canonical version of only-once is this:
(defmacro once-only ((&rest names) &body body)
(let ((gensyms (loop for n in names collect (gensym))))
`(let (,#(loop for g in gensyms collect `(,g (gensym))))
`(let (,,#(loop for g in gensyms for n in names collect ``(,,g ,,n)))
,(let (,#(loop for n in names for g in gensyms collect `(,n ,g)))
,#body)))))
The difference, as far as I can tell, is that the canonical version generates new symbols for every expansion of the macro using only-once. For example:
CL-USER> (macroexpand-1 '(once-only (foo) foo))
(LET ((#:G824 (GENSYM)))
`(LET (,`(,#:G824 ,FOO))
,(LET ((FOO #:G824))
FOO)))
T
CL-USER> (macroexpand-1 '(my-once-only (foo) foo))
`(LET (,`(,'#:G825 ,FOO))
,(LET ((FOO '#:G825))
FOO))
T
The variable my macro uses to store the value of foo is the same for every expansion of this form, in this case it would be #:G825. This is akin to defining a macro like the following:
(defmacro identity-except-for-bar (foo)
`(let ((bar 2))
,foo))
This macro captures bar, and this capture manifests when bar is passed to it, like so:
CL-USER> (let ((bar 1))
(identity-except-for-bar bar))
2
However, I cannot think of any way to pass #:G825 to a macro that uses my-only-once so that it breaks like this, because the symbols gensym returns are unique, and I cannot create a second copy of it outside of the macro. I assume that capturing it is unwanted, otherwise the canonical version wouldn't bother adding the additional layer of gensym. How could capturing a symbol like #:G826 be a problem? Please provide an example where this capture manifests.
We can demonstrate a behavioral difference between my-once-only and once-only:
Let's store our test form in a variable.
(defvar *form* '(lexalias a 0 (lexalias b (1+ a) (list a b))))
This test form exercises a macro called lexalias, which we will define in two ways. First with once-only:
(defmacro lexalias (var value &body body)
(once-only (value)
`(symbol-macrolet ((,var ,value))
,#body)))
(eval *form*) -> (0 1)
Then with my-once-only:
(defmacro lexalias (var value &body body)
(my-once-only (value)
`(symbol-macrolet ((,var ,value))
,#body)))
(eval *form*) -> (1 1)
Oops! The problem is that under my-once-only, both a and b end up being symbol-macrolet aliases for exactly the same gensym; the returned expression (list a b) ends up being something like (list #:g0025 #:g0025).
If you're writing a macro-writing helper that implements once-only evaluation, you have no idea how the symbol is going to be used by the code which calls the macro, whose author uses your once-only tool. There are two big unknowns: the nature of the macro and of its use.
As you can see, if you don't make fresh gensyms, it will not work correctly in all conceivable scenarios.

My lisp macro stops working in latest guile

I have macro that I've written in 2010, it was for managing structures like in Common Lips using Alists (here is whole file including functions https://jcubic.pl/struct.txt).
(define-macro (defstruct name . fields)
"Macro implementing structures in guile based on assoc list."
(let ((names (map (lambda (symbol) (gensym)) fields))
(struct (gensym))
(field-arg (gensym)))
`(if (not (every-unique ',fields))
(error 'defstruct "Fields must be unique")
(begin
(define (,(make-name name) ,#names)
(map cons ',fields (list ,#names)))
,#(map (lambda (field)
`(define (,(make-getter name field) ,struct)
(cdr (assq ',field ,struct)))) fields)
,#(map (lambda (field)
`(define (,(make-setter name field) ,struct ,field-arg)
(assq-set! ,struct ',field ,field-arg)
,field-arg)) fields)
(define (,(make-predicate name) ,struct)
(and (struct? ,struct)
(let ((result #t))
(for-each (lambda (x y)
(if (not (eq? x y)) (set! result #f)))
',fields
(map car ,struct))
result)))))))
It was working fine. I've recently updated this macro for my LIPS in JavaScript (it's based on scheme) and when I call it, it was returning false and wanted to know if this is how it would work in guile. But it turns out it don't work in guile at all. It shows this error:
While compiling expression: ERROR: Syntax error: unknown location:
definition in expression context, where definitions are not allowed,
in form (define (make-point #{ g746}# #{ g747}#) (map cons (quote (x
y)) (list #{ g746}# #{ g747}#))
Why I've got this error and how to fix it, so it work in guile again? I was long ago I don't remember how I was testing this code but opening guile using load function or copy paste the code into interpreter all give same error.
I'm using guile 2.0.14 on GNU/Linux.
PS: I prefer to use lisp macros IMO they are superior to weird scheme hygienic macros.
It looks like modern guile scheme does not see the begin in the if as a valid option to start a new definition context. This is perhaps a bug or better alignment of the scheme spec donough. But the following example code shows the technique to fix your code for more recent guile (you might need to create define-values as it is a more recent addition to guile. P.S. using lisps macros in guile is a clludge and it will get you into trouble if you plan to scheme a lot, the macros is like the parens, if you get used to it will feel natural.
Here is the code,
(define-macro (defstruct name . fields)
"Macro implementing structures in guile based on assoc list."
(let* ((names (map (lambda (symbol) (gensym)) fields))
(struct (gensym))
(field-arg (gensym))
(sname (make-name name))
(predname (make-predicate name))
(getnames (map (lambda (f) (make-getter name f)) fields))
(setnames (map (lambda (f) (make-setter name f)) fields)))
`(define-values (,sname ,predname ,#getnames ,#setnames)
(if (not (every-unique ',fields))
(error 'defstruct "Fields must be unique")
(let ()
(define (,sname ,#names)
(map cons ',fields (list ,#names)))
,#(map (lambda (field)
`(define (,(make-getter name field) ,struct)
(cdr (assq ',field ,struct)))) fields)
,#(map (lambda (field)
`(define (,(make-setter name field) ,struct ,field-arg)
(assq-set! ,struct ',field ,field-arg)
,field-arg)) fields)
(define (,predname ,struct)
(and (struct? ,struct)
(let ((result #t))
(for-each (lambda (x y)
(if (not (eq? x y)) (set! result #f)))
',fields
(map car ,struct))
result)))
(values ,sname ,predname ,#getnames ,#setnames))))))
Here is a version of define-values (look at the code after #' to see what it does)
(define-syntax define-values
(lambda (x)
(syntax-case x ()
((_ (f ...) code ...)
(with-syntax (((ff ...) (generate-temporaries #'(f ...))))
#'(begin
(define f #f)
...
(call-with-values (lambda () code ...)
(lambda (ff ...)
(set! f ff)
...))))))))

Racket Macro to auto-define functions given a list

I want to auto-generate a bunch of test functions from a list. The advantage being I can change the list (e.g. by reading in a CSV data table) and the program will auto-generate different tests on the next program execution.
For example, say I am trying to identify oxyanions in a string containing a chemical formula.
My list may be something like:
(define *oxyanion-tests*
; name cation
(list (list "aluminate" "Al")
(list "borate" "B")
(list "gallate" "Ga")
(list "germanate" "Ge")
(list "phosphate" "P")
(list "sulfate" "S")
(list "silicate" "Si")
(list "titanate" "Ti")
(list "vanadate" "V")
(list "stannate" "Sn")
(list "carbonate" "C")
(list "molybdate" "Mo")
(list "tungstate" "W")))
I'm reasonably confident that the chemical formula contains one of these oxyanions if there is a cation followed by an oxygen within parentheses (e.g. "(C O3)" ), or if the cation is followed by 2 or more oxygens (e.g. "C O3"). Note that this isn't perfect, since it will miss hypochlorite anions (e.g. "Cl O"), but it's good enough for my application.
(define ((*ate? elem) s-formula)
(or (regexp-match? (regexp (string-append "\\(" elem "[0-9.]* O[0-9.]*\\)")) s-formula)
(regexp-match? (regexp (string-append "(^| )" elem "[0-9.]* O[2-9][0-9.]*")) s-formula)))
I think I need a macro to do this, but I don't really understand how they work from reading the documentation. I'm asking here so that I have a good example to look at that is immediately useful to me.
Here is what I kind of think the macro should look like, but it doesn't work and I don't really have a mental model for figuring out how to fix it.
(require (for-syntax racket))
(define-syntax-rule (define-all/ate? oxyanion-tests)
(for ([test oxyanion-tests])
(match test
[(list name cation) (syntax->datum (syntax (define ((string->symbol (string-append name "?")) s-formula)
((*ate? cation) s-formula))))])))
Thanks for any guidance you can give me!
P.S. Here are a few tests that should pass:
(define-all/ate? *oxyanion-tests*)
(module+ test
(require rackunit)
(check-true (borate? "B O3"))
(check-true (carbonate? "C O3"))
(check-true (silicate? "Si O4")))
I see a couple of errors in your code:
Your *oxyanion-tests* is a runtime value, but you need its values to use as function name identifiers, so it must be available at compile time.
The syntax around the result of syntax-rules is implicit. So with syntax-rules, you only get the macro template language (see the docs for syntax for more info). Thus you can't do the datum->syntax that you are trying to do. You have to use syntax-case instead, which allows you to use all of Racket to compute the syntax objects you want.
Here's what I came up with:
#lang racket
(require (for-syntax racket/syntax)) ; for format-id
(define-for-syntax *oxyanion-tests*
; name cation
(list (list "aluminate" "Al")
(list "borate" "B")
(list "gallate" "Ga")
(list "germanate" "Ge")
(list "phosphate" "P")
(list "sulfate" "S")
(list "silicate" "Si")
(list "titanate" "Ti")
(list "vanadate" "V")
(list "stannate" "Sn")
(list "carbonate" "C")
(list "molybdate" "Mo")
(list "tungstate" "W")))
(define ((*ate? elem) s-formula)
(or (regexp-match?
(regexp (string-append "\\(" elem "[0-9.]* O[0-9.]*\\)"))
s-formula)
(regexp-match?
(regexp (string-append "(^| )" elem "[0-9.]* O[2-9][0-9.]*"))
s-formula)))
(define-syntax (define-all/ate? stx)
(syntax-case stx ()
[(_)
(let ([elem->fn-id
(λ (elem-str)
(format-id
stx "~a?"
(datum->syntax stx (string->symbol elem-str))))])
(with-syntax
([((ate? cation) ...)
(map
(λ (elem+cation)
(define elem (car elem+cation))
(define cation (cadr elem+cation))
(list (elem->fn-id elem) cation))
*oxyanion-tests*)])
#`(begin
(define (ate? sform) ((*ate? cation) sform))
...)))]))
(define-all/ate?)
(module+ test
(require rackunit)
(check-true (borate? "B O3"))
(check-true (carbonate? "C O3"))
(check-true (silicate? "Si O4")))
The key is the elem->fn-id function, which turns a string into a function identifier. It uses datum->syntax with stx as the context, meaning the defined function will be available in the context where the macro is invoked.

How do I define functions using Racket macros?

I am trying to write a macro that defines a special class of data structure with associated functions.
I know this is possible; it is done multiple times in the core language itself.
As a specific example, how would I define the define-struct macro in Scheme itself. It needs to create make-struct, struct-<<field>>, etc functions.
I tried doing this using define, however, this only defines the function in the macro's lexical scope.
How can I actually define a function in a macro?
The key for an answer is datum->syntax. The basic idea is that you want to take some random data and turn it into a syntax -- in this case, turn a symbol into an identifier. An identifier is basically a symbol with some lexical information that (very roughly) indicates how it is bound. Using datum->syntax you can do exactly that: it expects an existing piece of syntax which is where it copies the binding from, and a datum (a symbol here) which is the value that is contained in the syntax wrapper.
Here's an example that demonstrates a define-struct-like tool using this:
#lang scheme
;; implements a defstruct-like macro that uses association lists
(define-syntax (defstruct-lite stx)
(syntax-case stx ()
[(defstruct-lite name field ...)
(let ([make-id
(lambda (template . ids)
(let ([str (apply format template (map syntax->datum ids))])
(datum->syntax stx (string->symbol str))))])
(with-syntax ([make-name (make-id "make-~a" #'name)]
[name? (make-id "~a?" #'name)]
[(arg ...) (generate-temporaries #'(field ...))]
[(name-field ...)
(map (lambda (f) (make-id "~a-~a" #'name f))
(syntax->list #'(field ...)))])
#'(begin
(define (make-name arg ...) (list 'name (cons 'field arg) ...))
(define (name? x) (and (pair? x) (eq? 'name (car x))))
(define (name-field x)
(and (name? x) (cdr (assq 'field (cdr x)))))
...)))]))
And here's an example of using it:
(defstruct-lite point x y)
(point-y (make-point 1 2))