[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: boiler plate generation with hygenic macros in guile

From: Liliana Marie Prikler
Subject: Re: boiler plate generation with hygenic macros in guile
Date: Mon, 25 Jul 2022 14:35:28 +0200
User-agent: Evolution 3.42.1

Hi Blake,

Am Mittwoch, dem 20.07.2022 um 23:57 +0000 schrieb Blake Shaw:
> Ah! sorry, let me begin again:
> Right now I am working on a window manager extension system in Guile
> and GOOPs, and I want to eliminate the boilerplate for generating class
> slots, with a syntax-case macro like:
> #+begin_example scheme
> (define-syntax slot-machine
>     (λ (form)
>       (syntax-case form ()
>     ((_ category quality value)
>        #'(let* ((sym      (symbol-append category '- quality))
>         (set-sym! (symbol-append 'set- sym '!))
>         (get-sym  (symbol-append 'get- sym))
>         (acc-sym  (symbol-append 'acc- sym)))
>         (if (or (symbol? value) (string? value) (number? value))
>         (quasiquote (,sym #:init-value value
>                   #:setter       ,set-sym!
>                   #:getter       ,get-sym
>                   #:accessor   ,acc-sym))
>         (quasiquote (,sym #:init-form  value
>                   #:setter       ,set-sym!
>                   #:getter       ,get-sym
>                   #:accessor   ,acc-sym))))))))
> #+end_example
Now, first of all, I'd advise you to use (macroexpand '(expression))
whenever debugging your syntax-rules or syntax-cases.  In this example,
> (macroexpand (slot-machine 'inner 'quality #t))
> #<tree-il (call (toplevel inner-quality) (const #:init-form) (const #t)
> (const #:setter) (toplevel set-inner-quality!) (const #:getter)
> (toplevel get-inner-quality) (const #:accessor) (toplevel acc-inner-
> quality))>
but with an additional quote that's
> <tree-il (let (sym) (sym-1dff1b83541ce327-b4) ((call (toplevel symbol-
> append) (const inner) (const -) (const quality))) (let (set-sym!) (set-
> sym!-1dff1b83541ce327-b6) ((call (toplevel symbol-append) (const set-)
> (lexical sym sym-1dff1b83541ce327-b4) (const !))) (let (get-sym) (get-
> sym-1dff1b83541ce327-b8) ((call (toplevel symbol-append) (const get-)
> (lexical sym sym-1dff1b83541ce327-b4))) (let (acc-sym) (acc-sym-
> 1dff1b83541ce327-ba) ((call (toplevel symbol-append) (const acc-)
> (lexical sym sym-1dff1b83541ce327-b4))) (if (let (t) (t-
> 1dff1b83541ce327-bd) ((call (toplevel symbol?) (const #t))) (if
> (lexical t t-1dff1b83541ce327-bd) (lexical t t-1dff1b83541ce327-bd)
> (let (t) (t-1dff1b83541ce327-c0) ((call (toplevel string?) (const #t)))
> (if (lexical t t-1dff1b83541ce327-c0) (lexical t t-1dff1b83541ce327-c0)
> (call (toplevel number?) (const #t)))))) (call (@@ (guile) list)
> (lexical sym sym-1dff1b83541ce327-b4) (const #:init-value) (const #t)
> (const #:setter) (lexical set-sym! set-sym!-1dff1b83541ce327-b6) (const
> #:getter) (lexical get-sym get-sym-1dff1b83541ce327-b8) (const
> #:accessor) (lexical acc-sym acc-sym-1dff1b83541ce327-ba)) (call (@@
> (guile) list) (lexical sym sym-1dff1b83541ce327-b4) (const #:init-form)
> (const #t) (const #:setter) (lexical set-sym! set-sym!-
> 1dff1b83541ce327-b6) (const #:getter) (lexical get-sym get-sym-
> 1dff1b83541ce327-b8) (const #:accessor) (lexical acc-sym acc-sym-
> 1dff1b83541ce327-ba)))))))>

> With this I can call (slot-machine 'inner 'color "green") to produce
> what looks like an acceptable slot definition:
> => (inner-color #:init-value "#BF3D52" #:setter set-inner-color!
> #:getter get-inner-color #:accessor acc-inner-color)
Note that you're confusing symbols and syntax here.  A common mistake
for the novice macro expander, but in Scheme, symbols and syntax are

> Indeed, if I define a class with this slot definition in place, it
> works fine:
> #+begin_example scheme
> (define-class <dummy> ()
>     (inner-color #:init-value "#BF3D52" #:setter set-inner-color!
> #:getter
> get-inner-color #:accessor acc-inner-color))
> (describe <dummy>)
> => <dummy> is a class. It's an instance of <class>
> Superclasses are:
>     <object>
> Directs slots are:
>     inner-color
> (No direct subclass)
> Class Precedence List is:
>     <dummy>
>     <object>
>     <top>
> Class direct methods are:
>     Method #<<accessor-method> (<dummy> <top>) 7f7b27e10ac0>
>          Generic: setter:acc-inner-color
>     Specializers: <dummy> <top>
>     Method #<<accessor-method> (<dummy>) 7f7b27e10b00>
>          Generic: acc-inner-color
>     Specializers: <dummy>
>     Method #<<accessor-method> (<dummy> <top>) 7f7b27e10b40>
>          Generic: set-inner-color!
>     Specializers: <dummy> <top>
>     Method #<<accessor-method> (<dummy>) 7f7b27e10b80>
>          Generic: get-inner-color
>     Specializers: <dummy>
> #+end_example
> But if I try to use `slot-machine` inside a class definition i'm out
> of luck:
> (define-class <dummy> ()
>   (slot-machine 'inner 'color "green"))
> => While compiling expression:
> Syntax error:
> socket:7257:0: source expression failed to match any pattern in form
> (define-class-pre-definition ((quote inner) (quote color) "green"))

Now if we were to try and debug this with our newly found trick, we'd
quickly end up disappoined.  macroexpand can not expand this form for
the same reason as above: the source expression does not match any
pattern in define-class.  But what does this mean?
See, if you write your code as you did above, define-class will look at
the form (slot-machine 'inner 'quality #t) – in its unexpanded form! –
decide, that it can't handle it, and raise the above error.  Instead,
you need a macro which expands to
> (define-class <dummy> () (inner-quality #:init-value #t #:setter
> set-inner-quality! #:getter get-inner-quality #:accessor
> acc-inner-quality)))
which IOW means wrapping define-class in a macro.

As a handy little guide, see the macroexpand blow without define-class
actually defined.  Note the extra quote around (), which you'll have to
remove in your output.
> (macroexpand '(define-class <dummy> '() (inner-quality #:init-value
> #t #:setter set-inner-quality! #:getter get-inner-quality #:accessor
> acc-inner-quality)))
> $1 = #<tree-il (call (toplevel define-class) (toplevel <dummy>)
> (const ()) (call (toplevel inner-quality) (const #:init-value) (const
> #t) (const #:setter) (toplevel set-inner-quality!) (const #:getter)
> (toplevel get-inner-quality) (const #:accessor) (toplevel
> acc-inner-quality)))>

> I have tried to remedy this in a number of ways, using datum->syntax,
> quasisyntax/unsyntax, make-variable and by defining a new syntax-case
> macro to define classes, all without luck.
So we now arrive at the conclusion that only a syntax-rules/syntax-case
wrapper around define-class can remedy this, but you're still tasked
with the actual implementation.  Note, that the symbol-append of 'inner
'- 'quality actually gets you nothing and only restricts you.  I'd also
advise you to keep compatibility with define-class as much as possible,
that will significantly lessen your work.  Then you "only" need to add
your own special expansion rules with extra literals (literals go
between the first pairs of parentheses in syntax-rules/syntax-case).  


reply via email to

[Prev in Thread] Current Thread [Next in Thread]