Hi Maxime & Liliana, I just want to pop in and say thanks for your advice, its been helpful. I almost have the implementation down, but a few tricky bits I still havent got right... (what I'm attempting is a little more tricky than the example I gave here, which I shortened for the sake of transmissibility -- the goal is to be able to generate classes from dictionaries in an ad-hoc manner).
Feel like I'm almost there, just a few bits to work out, but I won't be able to return to it for another week or so. As soon as I have it down I'll report back in detail in case it could help others. Happy hacking! b On Mon, Jul 25, 2022 at 12:35 PM Liliana Marie Prikler < liliana.prik...@ist.tugraz.at> wrote: > > 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 > whenever debugging your syntax-rules or syntax-cases. In this example, > > (macroexpand (slot-machine 'inner 'quality #t)) > yields > > #<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)))))))> > Ouch. > > > 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 > distinct. > > > 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). > > > Cheers