2
votes

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.

1

1 Answers

2
votes

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)
                     ...))))))))