Skip to content

Issues with the hygienic macro system #526

Description

@jpellegrini

Hi @egallesio !
I'm opening this issue so we can track the problems with the hygienic macro system implementation.

This is what I can see...

Macros should remember their environment

(define-syntax f
  (syntax-rules ()
    ((f) a)))

(f)

The above should trigger an error, because the macro did not remember its environment. The "a" that it was referencing was "a in current module", and the local a included by the let should not be visible (this is a new local environment that did not exist a the time the macro was created).

Interestingly, Scheme9, Gambit, Cyclone and Bigloo also behave like that (but it's not according to the standard).

(define-syntax f
  (syntax-rules ()
    ((f a b) (- a b))))

(let ((f +)) (f 2 3))  ;; => should be 5;  *** STklos returns -1

Now Gambit does return 5... Cyclone and Bigloo return -1 (but the correct is 5).

(let ((x 'outer))
  (let-syntax ((m (syntax-rules ()
                    ((m a) (list x a)))))
    (let ((x 'inner))
      (cons x (m 17)))))

;; Should be (inner outer 17)
;; STklos result is (inner inner 17)

I think this problem is related to the eval issue (#410), since we'd have to eval the macro expander in the module where the macro was created.

STklos does not accept internal define-syntax

This is easy to fix, I suppose, since let-syntax already works.

Hygiene

This can be dealt with later, but I think it would be possible to either implement ER macros (there are issues with it, but I think we can circumvent them), or syntax-case (but I think a lexically-scoped define-macro along with gensym, syntax-rules and hygiene is as powerful as syntax-case, and perhaps easier -- then syntax-case can be implemented on top of this?)

The syntax-rules matcher

It's quite old (from Macros by Example), and there are some glitches. I can re-write it if it's ok.

In particular, the current implementation doesn't handle improper lists as patterns correctly (it complains about the length not being calculable). I think this is not hard to re-implement in a better way. -- fixed

It does not support templates beginning with ellipses.

Aliases, for SRFI 212

That SRFI specifies an alias macro that will work for any symbol - even those bound to macros.

STklos already has %symbol-alias, which does the right thing for global symbols (including macros!)

stklos> (define-macro (f . whatever) -1)
;; f
stklos> (f 1)
-1
stklos> (%symbol-alias 'g 'f)
stklos> (g 2)
-1

But it would be nice to be able to alias local symbols. For that, the scope structure in the compiler would have to be slightly adapted (I can do that):

Keep variable scopes and macro scopes separated as they are currently, but also create a third type of scope for aliases;

Both locals and mlocals in structure scope would be association lists:

  • ALIASES: the CAR is the symbol, and the CDR points to the location
    (a . LOCATION)
    where location is either some other entry for a variable in this scope (down the scope stack), or a binding to a specific symbol in a specific module. This works for both macros and variables.
  • MACROS: the CDR is the syntax object:
    (s . [#syntax ...])
  • LOCAL VARS: no need for a CDR
    (v)

This would give us a way to implement that SRFI, and would repect lexical scope for both variables, macros and aliases. :)

This is not difficult to do, I can help (I can make a PR soon if it's ok).

Tests for all the above

I have some tests... I'll organize them and make a draft PR for reference.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions