unofficial mirror of help-guix@gnu.org 
 help / color / mirror / Atom feed
From: Liliana Marie Prikler <liliana.prikler@ist.tugraz.at>
To: Blake Shaw <blake@sweatshoppe.org>, help-guix@gnu.org
Subject: Re: boiler plate generation with hygenic macros in guile
Date: Mon, 25 Jul 2022 14:35:28 +0200	[thread overview]
Message-ID: <85aa861298d2f7232f147c72ca65b706c08c5ce9.camel@ist.tugraz.at> (raw)
In-Reply-To: <CAKjmbcBOQ1P5=4Th7A4-j1DkhMtpqkCoJkgbO3OknP9rrpWSHA@mail.gmail.com>

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


  reply	other threads:[~2022-07-25 12:56 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
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 [this message]
2022-07-28 20:59     ` Blake Shaw

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://guix.gnu.org/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=85aa861298d2f7232f147c72ca65b706c08c5ce9.camel@ist.tugraz.at \
    --to=liliana.prikler@ist.tugraz.at \
    --cc=blake@sweatshoppe.org \
    --cc=help-guix@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).