schemeguile

finding an alternative for a let binding of a define-syntax


I'm in the process of trying to update some old guile 1.8 code to guile 3.x. I'm struggling to find a good replacement for a particular construct.

Here's an example that represents the old 1.8 code:

(define h (make-hash-table 31))

(define (def-node name-args types)
  (let ((name (car name-args))
        (args (cdr name-args)))
    (hashq-set! h name
                (list name args types))))

(define define-node
  (defmacro:syntax-transformer
    (lambda arg-list
      (apply def-node arg-list)
      #f)))

(define (make-nodes)
  (let ((def define-node))
    (def (a b . c) (BT . CT))
    (def (d e . f) (ET . FT))))

(make-nodes)

(display (hashq-ref h 'a))
(newline)
(display (hashq-ref h 'd))
(newline)

My hope is to update define-node and/or def-node while leaving make-nodes unchanged. So far I've rewritten define-node like this:

(define-syntax define-node
  (lambda (x)
    (syntax-case x ()
      [(_ name-args arg-types)
       #'(def-node 'name-args 'arg-types)])))

This seems to be a reasonable replacement for define-node, but it doesn't work with the current make-nodes, the let in make-nodes is not valid. I have to replace make-nodes with this:

(define (make-nodes)
    (define-node (a b . c) (BT . CT))
    (define-node (d e . f) (ET . FT)))

Which is OK, but I wonder if it's possible to find a solution where I don't have to modify make-nodes?


Solution

  • You'll need to change make-nodes. If you think about an expression like

    (let ((def define-node))
      (def (a b . c) (BT . CT))
      (def (d e . f) (ET . FT))))
    

    Which is the same as

    ((λ (def)
       (def (a b . c) (BT . CT))
       (def (d e . f) (ET . FT)))
     define-node)
    

    Then the evaluation of (def (a b . c) (BT . CT)) involves first evaluating the arguments ... which will fail.

    It's not clear to me how this ever could have worked with a sane version of let.

    So you'd need to use something like let-syntax to make a local macro. I don't know if Guile has that, but if not it should.


    The following will work, I think, in an R5RS Scheme, if you add make-hasheqv and hash-set! I tested this using Racket in fact, but using the R5RS module language with a suitable #%require to get the hashy bits:

    (define h (make-hasheqv))
    
    (define (def-node name-args types)
      (let ((name (car name-args))
            (args (cdr name-args)))
        (hash-set! h name
                    (list name args types))))
    
    (define (make-nodes)
      (let-syntax ((def (syntax-rules ()
                          ((_ name-args types)
                           (def-node 'name-args 'types)))))
        (def (a b . c) (BT . CT))
        (def (d e . f) (ET . FT))))