* boiler plate generation with hygenic macros in guile @ 2022-07-20 23:36 Blake Shaw 2022-07-20 23:57 ` Blake Shaw 0 siblings, 1 reply; 4+ messages in thread From: Blake Shaw @ 2022-07-20 23:36 UTC (permalink / raw) To: help-guix Hi folks, ^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: boiler plate generation with hygenic macros in guile 2022-07-20 23:36 boiler plate generation with hygenic macros in guile Blake Shaw @ 2022-07-20 23:57 ` Blake Shaw 2022-07-25 12:35 ` Liliana Marie Prikler 0 siblings, 1 reply; 4+ messages in thread From: Blake Shaw @ 2022-07-20 23:57 UTC (permalink / raw) To: help-guix 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 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) 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")) I have tried to remedy this ina number of ways, using datum->syntax, quasisyntax/unsyntax, make-variable and by defining a new syntax-case macro to define classes, all without luck. This is actually a recurring theme with my experience with Guile, working on a project, needing to generate boilerplate, and then being unable to find a result, so I figured its time I reach out to figure out what I'm doing wrong in this situation. Thanks for your help! Best, Blake ^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: boiler plate generation with hygenic macros in guile 2022-07-20 23:57 ` Blake Shaw @ 2022-07-25 12:35 ` Liliana Marie Prikler 2022-07-28 20:59 ` Blake Shaw 0 siblings, 1 reply; 4+ messages in thread From: Liliana Marie Prikler @ 2022-07-25 12:35 UTC (permalink / raw) To: Blake Shaw, help-guix 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)) 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 ^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: boiler plate generation with hygenic macros in guile 2022-07-25 12:35 ` Liliana Marie Prikler @ 2022-07-28 20:59 ` Blake Shaw 0 siblings, 0 replies; 4+ messages in thread From: Blake Shaw @ 2022-07-28 20:59 UTC (permalink / raw) To: Liliana Marie Prikler; +Cc: help-guix, Maxime Devos 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.prikler@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 ^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2022-07-28 20:59 UTC | newest] Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2022-07-20 23:36 boiler plate generation with hygenic macros in guile Blake Shaw 2022-07-20 23:57 ` Blake Shaw 2022-07-25 12:35 ` Liliana Marie Prikler 2022-07-28 20:59 ` Blake Shaw
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).