unofficial mirror of guile-user@gnu.org 
 help / color / mirror / Atom feed
* macroexpand-1
@ 2018-05-21 15:48 Catonano
  2018-05-29 15:01 ` macroexpand-1 Mark H Weaver
  0 siblings, 1 reply; 8+ messages in thread
From: Catonano @ 2018-05-21 15:48 UTC (permalink / raw)
  To: Guile User

in the NEWS file, I read:


...
** Removed function: `macroexpand-1'

It is unclear how to implement `macroexpand-1' with syntax-case, though
PLT Scheme does prove that it is possible.




what's the problem with macroexpand-1 and syntax-case ?

Thanks


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: macroexpand-1
  2018-05-21 15:48 macroexpand-1 Catonano
@ 2018-05-29 15:01 ` Mark H Weaver
  2018-05-29 19:01   ` macroexpand-1 Catonano
  0 siblings, 1 reply; 8+ messages in thread
From: Mark H Weaver @ 2018-05-29 15:01 UTC (permalink / raw)
  To: Catonano; +Cc: Guile User

Hi,

Catonano <catonano@gmail.com> writes:

> in the NEWS file, I read:
>
>
> ...
> ** Removed function: `macroexpand-1'
>
> It is unclear how to implement `macroexpand-1' with syntax-case, though
> PLT Scheme does prove that it is possible.
>
>
> what's the problem with macroexpand-1 and syntax-case ?

In Guile 1.x, 'macroexpand-1' performed a single macro expansion step at
the top-level form of an expression, using its old non-hygienic macro
expander.  There are several problems with trying to provide such an
interface in a Hygienic macro expander, and especially in the
'syntax-case' expander with its support for 'datum->syntax'.  For one
thing, our modern macro expander doesn't even work with the plain
S-expressions which 'macroexpand-1' accepted and produced.  It works
with "syntax objects", which effectively annotate every identifier with
extra information needed to determine which binding it references, and
also extra information needed to implement 'datum->syntax'.  This in
turn requires detailed knowledge of the lexical environment in which
expansion is taking place, whereas 'macroexpand-1' provides no way for
the user to provide this information.

       Mark



^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: macroexpand-1
  2018-05-29 15:01 ` macroexpand-1 Mark H Weaver
@ 2018-05-29 19:01   ` Catonano
  2018-05-29 19:01     ` macroexpand-1 Catonano
  2018-05-30  1:07     ` macroexpand-1 Mark H Weaver
  0 siblings, 2 replies; 8+ messages in thread
From: Catonano @ 2018-05-29 19:01 UTC (permalink / raw)
  To: Mark H Weaver; +Cc: Guile User

Mark,

2018-05-29 17:01 GMT+02:00 Mark H Weaver <mhw@netris.org>:

> Hi,
>
> Catonano <catonano@gmail.com> writes:
>
> > in the NEWS file, I read:
> >
> >
> > ...
> > ** Removed function: `macroexpand-1'
> >
> > It is unclear how to implement `macroexpand-1' with syntax-case, though
> > PLT Scheme does prove that it is possible.
> >
> >
> > what's the problem with macroexpand-1 and syntax-case ?
>
> In Guile 1.x, 'macroexpand-1' performed a single macro expansion step at
> the top-level form of an expression, using its old non-hygienic macro
> expander.  There are several problems with trying to provide such an
> interface in a Hygienic macro expander, and especially in the
> 'syntax-case' expander with its support for 'datum->syntax'.  For one
> thing, our modern macro expander doesn't even work with the plain
> S-expressions which 'macroexpand-1' accepted and produced.  It works
> with "syntax objects", which effectively annotate every identifier with
> extra information needed to determine which binding it references, and
> also extra information needed to implement 'datum->syntax'.  This in
> turn requires detailed knowledge of the lexical environment in which
> expansion is taking place, whereas 'macroexpand-1' provides no way for
> the user to provide this information.
>
>        Mark
>

I have been reading this document about the scheme higienic macros
https://www.cs.indiana.edu/~dyb/pubs/bc-syntax-case.pdf

I stopped reading it when I read that the implementation relies on a
previously bootstrapped version of another macro expansion implementation.

So I have a general overview of the issue

But Racket has some facilities to step and debug macros, as you can see here
https://docs.racket-lang.org/macro-debugger/index.html

Aren' t Racket macros higienyc ?

In this question I've been promptly suggested a quick solution to perform a
single macro expansion step

https://stackoverflow.com/questions/50073207/macro-expansion-in-guile-scheme/50515880#50515880


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: macroexpand-1
  2018-05-29 19:01   ` macroexpand-1 Catonano
@ 2018-05-29 19:01     ` Catonano
  2018-05-30  1:07     ` macroexpand-1 Mark H Weaver
  1 sibling, 0 replies; 8+ messages in thread
From: Catonano @ 2018-05-29 19:01 UTC (permalink / raw)
  To: Mark H Weaver; +Cc: Guile User

Anyway: thank you !!


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: macroexpand-1
  2018-05-29 19:01   ` macroexpand-1 Catonano
  2018-05-29 19:01     ` macroexpand-1 Catonano
@ 2018-05-30  1:07     ` Mark H Weaver
  2018-05-31  8:21       ` macroexpand-1 Catonano
  1 sibling, 1 reply; 8+ messages in thread
From: Mark H Weaver @ 2018-05-30  1:07 UTC (permalink / raw)
  To: Catonano; +Cc: Guile User

Hi,

Catonano <catonano@gmail.com> writes:

> 2018-05-29 17:01 GMT+02:00 Mark H Weaver <mhw@netris.org>:
>  > what's the problem with macroexpand-1 and syntax-case ?
>
>  In Guile 1.x, 'macroexpand-1' performed a single macro expansion step at
>  the top-level form of an expression, using its old non-hygienic macro
>  expander.  There are several problems with trying to provide such an
>  interface in a Hygienic macro expander, and especially in the
>  'syntax-case' expander with its support for 'datum->syntax'.  For one
>  thing, our modern macro expander doesn't even work with the plain
>  S-expressions which 'macroexpand-1' accepted and produced.  It works
>  with "syntax objects", which effectively annotate every identifier with
>  extra information needed to determine which binding it references, and
>  also extra information needed to implement 'datum->syntax'.  This in
>  turn requires detailed knowledge of the lexical environment in which
>  expansion is taking place, whereas 'macroexpand-1' provides no way for
>  the user to provide this information.
>
>         Mark
>
> I have been reading this document about the scheme higienic macros
> https://www.cs.indiana.edu/~dyb/pubs/bc-syntax-case.pdf
>
> I stopped reading it when I read that the implementation relies on a
> previously bootstrapped version of another macro expansion
> implementation.

That's not an inherent limitation of the 'syntax-case' design.  It's
merely an unfortunate attribute of the psyntax _implementation_ of
'syntax-case', apparently because they didn't care enough about
bootstrapping issues to write psyntax without the benefit of macros.

'syntax-case' could certainly be implemented without using a
pre-existing macro expander.

> But Racket has some facilities to step and debug macros, as you can
> see here https://docs.racket-lang.org/macro-debugger/index.html
>
> Aren' t Racket macros higienyc ?

Yes, of course, and we could certainly implement similar macro stepping
facilities in Guile.  But that's not what you asked about in your
previous message.  You asked about 'macroexpand-1', and my answer was
specifically about that.  I don't see any procedure similar to
'macroexpand-1' in the document you referenced above.

> In this question I've been promptly suggested a quick solution to
> perform a single macro expansion step
>
> https://stackoverflow.com/questions/50073207/macro-expansion-in-guile-scheme/50515880#50515880

For posterity, here's the quick solution suggested in the link above:

  (define-syntax (expand1 stx)
    (syntax-case stx ()
      [(_expand1 form)
       (syntax-case #'form ()
         [(id . more)
          (identifier? #'id)
          (let ([transformer (syntax-local-value #'id)])          
            (with-syntax ([expansion (transformer #'form)])
              #''expansion))]
         [_
          #''form])]))

This is just a toy, and not very useful in practice.
Here's the equivalent formulation for Guile:

  (use-modules (system syntax)
               (srfi srfi-11))
  
  (define (syntax-local-value id)
    (let-values (((type value) (syntax-local-binding id)))
      value))
  
  (define-syntax expand1
    (lambda (stx)
      (syntax-case stx ()
        [(_expand1 form)
         (syntax-case #'form ()
           [(id . more)
            (identifier? #'id)
            (let ([transformer (syntax-local-value #'id)])
              (with-syntax ([expansion (transformer #'form)])
                #''expansion))]
           [_
            #''form])])))

(I usually prefer to avoid using square brackets in this way, but for
sake of comparison, I used them in the definition of 'expand1' above.)

Anyway, it works the same way as in Racket for this simple example:

  scheme@(guile-user)> (expand1 (or 1 2 3))
  $2 = (let ((t 1)) (if t t (or 2 3)))

So, what's the problem?  The first problem is that when quoting the
resulting expansion, the binding information associated with identifiers
in the syntax objects are lost, so hygiene is lost.  For example:

  scheme@(guile-user)> (expand1 (or 1 2 t))
  $3 = (let ((t 1)) (if t t (or 2 t)))

Moving on, let's use this to try to investigate how 'define-record-type'
works from SRFI-9 in Guile:

  scheme@(guile-user)> ,use (srfi srfi-9)
  scheme@(guile-user)> (expand1 (define-record-type <box>
                                  (box value)
                                  box?
                                  (value unbox set-box!)))
  $4 = (%define-record-type #f (define-record-type <box> (box value) box? (value unbox set-box!)) <box> (box value) box? (value unbox set-box!))
  scheme@(guile-user)> (expand1 (%define-record-type #f (define-record-type <box> (box value) box? (value unbox set-box!)) <box> (box value) box? (value unbox set-box!)))
  While compiling expression:
  Wrong type to apply: (%define-record-type guile-user)
  scheme@(guile-user)>

So what went wrong here?  The problem is that '%define-record-type' is a
private macro, used internally within (srfi srfi-9), and therefore not
bound in the (guile-user) module where I'm working.  If we had been
working with syntax objects, each identifier within the expression would
have been annotated with the specific binding that it refers to, but as
I noted above, that information has been stripped.

The awkward error message is because this toy implementation doesn't
check if the identifier is a macro or not.

One way we could try to improve this is to write 'expandN', which
performs N macro expansion steps, keeping them as syntax objects during
the intermediate steps:

  (use-modules (system syntax)
               (srfi srfi-11))
  
  (define (syntax-local-type id)
    (let-values (((type value) (syntax-local-binding id)))
      type))
  
  (define (syntax-local-value id)
    (let-values (((type value) (syntax-local-binding id)))
      value))
  
  (define-syntax expandN
    (lambda (stx)
      (syntax-case stx ()
        ((_expandN n form)
         (let ((n (syntax->datum #'n)))
           (and (number? n) (integer? n)))
         (let ((n (syntax->datum #'n)))
           (if (positive? n)
               (syntax-case #'form ()
                 ((id . _)
                  (and (identifier? #'id)
                       (eq? 'macro (syntax-local-type #'id)))
                  (let ((transformer (syntax-local-value #'id)))
                    (with-syntax ((expansion (transformer #'form))
                                  (n-1 (datum->syntax #'id (- n 1))))
                      #'(expandN n-1 expansion))))
                 (_
                  #''form))
               #''form))))))

Unfortunately, this is not quite right, because it fails to add "marks"
to the identifiers introduced by the macro transformers, and thus is not
fully hygienic, and variable capture may occur.  However, it is better
than what we had before, and good enough to step further into
'define-record-type':

--8<---------------cut here---------------start------------->8---
scheme@(guile-user)> ,pp (expandN 0 (define-record-type <box>
                                      (box value)
                                      box?
                                      (value unbox set-box!)))
$2 = (define-record-type
  <box>
  (box value)
  box?
  (value unbox set-box!))
scheme@(guile-user)> ,pp (expandN 1 (define-record-type <box>
                                      (box value)
                                      box?
                                      (value unbox set-box!)))
$3 = (%define-record-type
  #f
  (define-record-type
    <box>
    (box value)
    box?
    (value unbox set-box!))
  <box>
  (box value)
  box?
  (value unbox set-box!))
scheme@(guile-user)> ,pp (expandN 2 (define-record-type <box>
                                      (box value)
                                      box?
                                      (value unbox set-box!)))
$4 = (begin
  (define-inlinable
    (box value)
    (let ((s (allocate-struct <box> 1)))
      (struct-set! s 0 value)
      s))
  (define <box>
    (let ((rtd (make-struct/no-tail
                 record-type-vtable
                 'pw
                 default-record-printer
                 '<box>
                 '(value))))
      (set-struct-vtable-name! rtd '<box>)
      (struct-set! rtd (+ 2 vtable-offset-user) box)
      rtd))
  (define-inlinable
    (box? obj)
    (and (struct? obj)
         (eq? (struct-vtable obj) <box>)))
  (define-tagged-inlinable
    ((%%type <box>)
     (%%index 0)
     (%%copier %%<box>-set-fields))
    (unbox s)
    (if (eq? (struct-vtable s) <box>)
      (struct-ref s 0)
      (throw-bad-struct s 'unbox)))
  (define-syntax-rule
    (%%<box>-set-fields check? s (getter expr) ...)
    (%%set-fields
      <box>
      (unbox)
      check?
      s
      (getter expr)
      ...))
  (define-inlinable
    (set-box! s val)
    (if (eq? (struct-vtable s) <box>)
      (struct-set! s 0 val)
      (throw-bad-struct s 'set-box!))))
scheme@(guile-user)>
--8<---------------cut here---------------end--------------->8---

Unfortunately this is as far as we can go with 'expandN', because it
only expands macros at the top-level of the expression.  In this case,
the top-level expression is a 'begin' form, which is a core form.  At
this point, a real macro expander descends into the core form and
expands subexpressions, but in order to do this properly, it needs to
understand the meanings of the core forms that it's descending into.

For example, when descending into a 'let' form, it needs to take note of
the variables that are bound by the 'let'.  For example:

  scheme@(guile-user)> (expandN 0 (or 1 2 3))
  $2 = (or 1 2 3)
  scheme@(guile-user)> (expandN 1 (or 1 2 3))
  $3 = (let ((t 1)) (if t t (or 2 3)))
  scheme@(guile-user)> (expandN 2 (or 1 2 3))
  $4 = (let ((t 1)) (if t t (or 2 3)))

The last two outputs are the same because I made 'expandN' just smart
enough to notice that 'let' is not a macro, in which case it stops
gracefully without triggering an exception.

Hopefully this illustrates why the old 'macroexpand-1' procedure from
Guile 1.x, which works on plain S-expressions without extra binding
information, and which only expands macros at the top level of the
expression, cannot be usefully implemented on a modern hygienic macro
expander.

However, what certainly *could* be done is some kind of interactive tool
to incrementally step macro expansions, while keeping track of the
syntax objects behind the scenes.  To be useful in realistic cases, it
would need to understand most if not all of the core forms in Guile.
Those core forms are the ones defined using 'global-extend' in
psyntax.scm.

     Regards,
       Mark



^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: macroexpand-1
  2018-05-30  1:07     ` macroexpand-1 Mark H Weaver
@ 2018-05-31  8:21       ` Catonano
  2018-06-03 14:04         ` macroexpand-1 Mark H Weaver
  0 siblings, 1 reply; 8+ messages in thread
From: Catonano @ 2018-05-31  8:21 UTC (permalink / raw)
  To: Mark H Weaver; +Cc: Guile User

Mark,

thank you very much for explaining at lenght, I appreciate that !

2018-05-30 3:07 GMT+02:00 Mark H Weaver <mhw@netris.org>:

> Hi,
>
> Catonano <catonano@gmail.com> writes:
>
> > 2018-05-29 17:01 GMT+02:00 Mark H Weaver <mhw@netris.org>:
> >  > what's the problem with macroexpand-1 and syntax-case ?
> >
> >  In Guile 1.x, 'macroexpand-1' performed a single macro expansion step at
> >  the top-level form of an expression, using its old non-hygienic macro
> >  expander.  There are several problems with trying to provide such an
> >  interface in a Hygienic macro expander, and especially in the
> >  'syntax-case' expander with its support for 'datum->syntax'.  For one
> >  thing, our modern macro expander doesn't even work with the plain
> >  S-expressions which 'macroexpand-1' accepted and produced.  It works
> >  with "syntax objects", which effectively annotate every identifier with
> >  extra information needed to determine which binding it references, and
> >  also extra information needed to implement 'datum->syntax'.  This in
> >  turn requires detailed knowledge of the lexical environment in which
> >  expansion is taking place, whereas 'macroexpand-1' provides no way for
> >  the user to provide this information.
> >
> >         Mark
> >
> > I have been reading this document about the scheme higienic macros
> > https://www.cs.indiana.edu/~dyb/pubs/bc-syntax-case.pdf
> >
> > I stopped reading it when I read that the implementation relies on a
> > previously bootstrapped version of another macro expansion
> > implementation.
>
> That's not an inherent limitation of the 'syntax-case' design.  It's
> merely an unfortunate attribute of the psyntax _implementation_ of
> 'syntax-case', apparently because they didn't care enough about
> bootstrapping issues to write psyntax without the benefit of macros.
>
> 'syntax-case' could certainly be implemented without using a
> pre-existing macro expander.
>
> > But Racket has some facilities to step and debug macros, as you can
> > see here https://docs.racket-lang.org/macro-debugger/index.html
> >
> > Aren' t Racket macros higienyc ?
>
> Yes, of course, and we could certainly implement similar macro stepping
> facilities in Guile.  But that's not what you asked about in your
> previous message.  You asked about 'macroexpand-1', and my answer was
> specifically about that.  I don't see any procedure similar to
> 'macroexpand-1' in the document you referenced above.
>

My bad

I assumed that macroexpand-1 was the building block for Racket macro
stepping and inspecting tools

I' m interested in macro stepping and inspecting facilities, not in
macroexpand-1 per se


> > In this question I've been promptly suggested a quick solution to
> > perform a single macro expansion step
> >
> > https://stackoverflow.com/questions/50073207/macro-
> expansion-in-guile-scheme/50515880#50515880
>
> For posterity, here's the quick solution suggested in the link above:
>
>   (define-syntax (expand1 stx)
>     (syntax-case stx ()
>       [(_expand1 form)
>        (syntax-case #'form ()
>          [(id . more)
>           (identifier? #'id)
>           (let ([transformer (syntax-local-value #'id)])
>             (with-syntax ([expansion (transformer #'form)])
>               #''expansion))]
>          [_
>           #''form])]))
>
> This is just a toy, and not very useful in practice.
> Here's the equivalent formulation for Guile:
>
>   (use-modules (system syntax)
>                (srfi srfi-11))
>
>   (define (syntax-local-value id)
>     (let-values (((type value) (syntax-local-binding id)))
>       value))
>
>   (define-syntax expand1
>     (lambda (stx)
>       (syntax-case stx ()
>         [(_expand1 form)
>          (syntax-case #'form ()
>            [(id . more)
>             (identifier? #'id)
>             (let ([transformer (syntax-local-value #'id)])
>               (with-syntax ([expansion (transformer #'form)])
>                 #''expansion))]
>            [_
>             #''form])])))
>
> (I usually prefer to avoid using square brackets in this way, but for
> sake of comparison, I used them in the definition of 'expand1' above.)
>
> Anyway, it works the same way as in Racket for this simple example:
>
>   scheme@(guile-user)> (expand1 (or 1 2 3))
>   $2 = (let ((t 1)) (if t t (or 2 3)))
>
>
This is surprising to me

When I saw that example made in Racket for the first time I instantly
identified "syntax-local-value" as problematic

Will Guile have anything equivalent ? I asked myself

Now you show me the "(system syntax)" namespace (or module)

I didn't  suspect it existed

Does the manual mention it anywhere ? I didn' t see it

Or maybe does it belong to any scheme standard ?

Do any more (system ....) namespaces exist ?

How would I know ?


> So, what's the problem?  The first problem is that when quoting the
> resulting expansion, the binding information associated with identifiers
> in the syntax objects are lost, so hygiene is lost.  For example:
>
>   scheme@(guile-user)> (expand1 (or 1 2 t))
>   $3 = (let ((t 1)) (if t t (or 2 t)))
>
> Moving on, let's use this to try to investigate how 'define-record-type'
> works from SRFI-9 in Guile:
>
>   scheme@(guile-user)> ,use (srfi srfi-9)
>   scheme@(guile-user)> (expand1 (define-record-type <box>
>                                   (box value)
>                                   box?
>                                   (value unbox set-box!)))
>


>   $4 = (%define-record-type #f (define-record-type <box> (box value) box?
> (value unbox set-box!)) <box> (box value) box? (value unbox set-box!))
>


>   scheme@(guile-user)> (expand1 (%define-record-type #f
> (define-record-type <box> (box value) box? (value unbox set-box!)) <box>
> (box value) box? (value unbox set-box!)))
>   While compiling expression:
>   Wrong type to apply: (%define-record-type guile-user)
>   scheme@(guile-user)>
>
> So what went wrong here?  The problem is that '%define-record-type' is a
> private macro, used internally within (srfi srfi-9), and therefore not
> bound in the (guile-user) module where I'm working.  If we had been
> working with syntax objects, each identifier within the expression would
> have been annotated with the specific binding that it refers to, but as
> I noted above, that information has been stripped.
>
> The awkward error message is because this toy implementation doesn't
> check if the identifier is a macro or not.
>
> One way we could try to improve this is to write 'expandN', which
> performs N macro expansion steps, keeping them as syntax objects during
> the intermediate steps:
>
>   (use-modules (system syntax)
>                (srfi srfi-11))
>
>   (define (syntax-local-type id)
>     (let-values (((type value) (syntax-local-binding id)))
>       type))
>
>   (define (syntax-local-value id)
>     (let-values (((type value) (syntax-local-binding id)))
>       value))
>
>   (define-syntax expandN
>     (lambda (stx)
>       (syntax-case stx ()
>         ((_expandN n form)
>          (let ((n (syntax->datum #'n)))
>            (and (number? n) (integer? n)))
>          (let ((n (syntax->datum #'n)))
>            (if (positive? n)
>                (syntax-case #'form ()
>                  ((id . _)
>                   (and (identifier? #'id)
>                        (eq? 'macro (syntax-local-type #'id)))
>                   (let ((transformer (syntax-local-value #'id)))
>                     (with-syntax ((expansion (transformer #'form))
>                                   (n-1 (datum->syntax #'id (- n 1))))
>                       #'(expandN n-1 expansion))))
>                  (_
>                   #''form))
>                #''form))))))
>
> Unfortunately, this is not quite right, because it fails to add "marks"
> to the identifiers introduced by the macro transformers, and thus is not
> fully hygienic, and variable capture may occur.  However, it is better
> than what we had before, and good enough to step further into
> 'define-record-type':
>
> --8<---------------cut here---------------start------------->8---
> scheme@(guile-user)> ,pp (expandN 0 (define-record-type <box>
>                                       (box value)
>                                       box?
>                                       (value unbox set-box!)))
> $2 = (define-record-type
>   <box>
>   (box value)
>   box?
>   (value unbox set-box!))
> scheme@(guile-user)> ,pp (expandN 1 (define-record-type <box>
>                                       (box value)
>                                       box?
>                                       (value unbox set-box!)))
> $3 = (%define-record-type
>   #f
>   (define-record-type
>     <box>
>     (box value)
>     box?
>     (value unbox set-box!))
>   <box>
>   (box value)
>   box?
>   (value unbox set-box!))
> scheme@(guile-user)> ,pp (expandN 2 (define-record-type <box>
>                                       (box value)
>                                       box?
>                                       (value unbox set-box!)))
> $4 = (begin
>   (define-inlinable
>     (box value)
>     (let ((s (allocate-struct <box> 1)))
>       (struct-set! s 0 value)
>       s))
>   (define <box>
>     (let ((rtd (make-struct/no-tail
>                  record-type-vtable
>                  'pw
>                  default-record-printer
>                  '<box>
>                  '(value))))
>       (set-struct-vtable-name! rtd '<box>)
>       (struct-set! rtd (+ 2 vtable-offset-user) box)
>       rtd))
>   (define-inlinable
>     (box? obj)
>     (and (struct? obj)
>          (eq? (struct-vtable obj) <box>)))
>   (define-tagged-inlinable
>     ((%%type <box>)
>      (%%index 0)
>      (%%copier %%<box>-set-fields))
>     (unbox s)
>     (if (eq? (struct-vtable s) <box>)
>       (struct-ref s 0)
>       (throw-bad-struct s 'unbox)))
>   (define-syntax-rule
>     (%%<box>-set-fields check? s (getter expr) ...)
>     (%%set-fields
>       <box>
>       (unbox)
>       check?
>       s
>       (getter expr)
>       ...))
>   (define-inlinable
>     (set-box! s val)
>     (if (eq? (struct-vtable s) <box>)
>       (struct-set! s 0 val)
>       (throw-bad-struct s 'set-box!))))
> scheme@(guile-user)>
> --8<---------------cut here---------------end--------------->8---
>
> Unfortunately this is as far as we can go with 'expandN', because it
> only expands macros at the top-level of the expression.  In this case,
> the top-level expression is a 'begin' form, which is a core form.  At
> this point, a real macro expander descends into the core form and
> expands subexpressions, but in order to do this properly, it needs to
> understand the meanings of the core forms that it's descending into.
>
> For example, when descending into a 'let' form, it needs to take note of
> the variables that are bound by the 'let'.  For example:
>
>   scheme@(guile-user)> (expandN 0 (or 1 2 3))
>   $2 = (or 1 2 3)
>   scheme@(guile-user)> (expandN 1 (or 1 2 3))
>   $3 = (let ((t 1)) (if t t (or 2 3)))
>   scheme@(guile-user)> (expandN 2 (or 1 2 3))
>   $4 = (let ((t 1)) (if t t (or 2 3)))
>
> The last two outputs are the same because I made 'expandN' just smart
> enough to notice that 'let' is not a macro, in which case it stops
> gracefully without triggering an exception.
>
> Hopefully this illustrates why the old 'macroexpand-1' procedure from
> Guile 1.x, which works on plain S-expressions without extra binding
> information, and which only expands macros at the top level of the
> expression, cannot be usefully implemented on a modern hygienic macro
> expander.
>
> However, what certainly *could* be done is some kind of interactive tool
> to incrementally step macro expansions, while keeping track of the
> syntax objects behind the scenes.  To be useful in realistic cases, it
> would need to understand most if not all of the core forms in Guile.
> Those core forms are the ones defined using 'global-extend' in
> psyntax.scm.
>
>      Regards,
>        Mark
>

I had read about those bindings and marks in the paper but I had no clear
idea about what they were

Now I have a way better idea

Also, now I understand what would be required in order to implement some
macro stepping and inspecting facilities similar to those available in
Racket

I don' t know if I will actually try to implement anything, but I will
certainly use these notions to my advantage in working with Guile/Guix

So thanks


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: macroexpand-1
  2018-05-31  8:21       ` macroexpand-1 Catonano
@ 2018-06-03 14:04         ` Mark H Weaver
  2018-06-05  7:11           ` macroexpand-1 Catonano
  0 siblings, 1 reply; 8+ messages in thread
From: Mark H Weaver @ 2018-06-03 14:04 UTC (permalink / raw)
  To: Catonano; +Cc: Guile User

Hi,

Catonano <catonano@gmail.com> writes:

> 2018-05-30 3:07 GMT+02:00 Mark H Weaver <mhw@netris.org>:
>
>  This is just a toy, and not very useful in practice.
>  Here's the equivalent formulation for Guile:
>
>    (use-modules (system syntax)
>                 (srfi srfi-11))
>
>    (define (syntax-local-value id)
>      (let-values (((type value) (syntax-local-binding id)))
>        value))
>
>    (define-syntax expand1
>      (lambda (stx)
>        (syntax-case stx ()
>          [(_expand1 form)
>           (syntax-case #'form ()
>             [(id . more)
>              (identifier? #'id)
>              (let ([transformer (syntax-local-value #'id)])
>                (with-syntax ([expansion (transformer #'form)])
>                  #''expansion))]
>             [_
>              #''form])])))
>
>  (I usually prefer to avoid using square brackets in this way, but for
>  sake of comparison, I used them in the definition of 'expand1' above.)
>
>  Anyway, it works the same way as in Racket for this simple example:
>
>    scheme@(guile-user)> (expand1 (or 1 2 3))
>    $2 = (let ((t 1)) (if t t (or 2 3)))
>
> This is surprising to me
>
> When I saw that example made in Racket for the first time I instantly
> identified "syntax-local-value" as problematic

You're right, it is problematic, and it's good that you noticed that.
It exposes internal details of Guile's implementation, which is quite
likely to change in the future.  Do not use this interface if you can
avoid it, and expect code that uses it to break in future versions of
Guile.  That said, it can be useful for writing things like macro
steppers.

> Will Guile have anything equivalent ? I asked myself
>
> Now you show me the "(system syntax)" namespace (or module)
>
> I didn't  suspect it existed
>
> Does the manual mention it anywhere ? I didn' t see it

Do you know how to search the manual or its index?  Press 'i' from
either the Emacs or standalone info browsers to search the index, where
you can find 'syntax-local-binding'.

You can also search the entire manual text by pressing 's'.  You can
find (system syntax) that way.

> Or maybe does it belong to any scheme standard ?

No, certainly not.

> Do any more (system ....) namespaces exist ? 
>
> How would I know ?

Look in the "module" subdirectory of the Guile source tree for modules
that come with Guile itself, or more generally in the directories of
%load-path after installation.  The directory structure mirrors the
module namespaces.  The module (foo bar baz) is found in
<DIR>/foo/bar/baz.scm, where <DIR> is a component of %load-path.  For
example, (system syntax) is in <DIR>/system/syntax.scm.  In the Guile
source tree, it's in module/system/syntax.scm.

      Mark



^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: macroexpand-1
  2018-06-03 14:04         ` macroexpand-1 Mark H Weaver
@ 2018-06-05  7:11           ` Catonano
  0 siblings, 0 replies; 8+ messages in thread
From: Catonano @ 2018-06-05  7:11 UTC (permalink / raw)
  To: Mark H Weaver; +Cc: Guile User

Mark,

thanks again for your remarks

> 2018-05-30 3:07 GMT+02:00 Mark H Weaver <mhw@netris.org>:
>
>
You're right, it is problematic, and it's good that you noticed that.
> It exposes internal details of Guile's implementation, which is quite
> likely to change in the future.  Do not use this interface if you can
> avoid it, and expect code that uses it to break in future versions of
> Guile.  That said, it can be useful for writing things like macro
> steppers.
>

Well, if it's gonna change, than a macro stepper that relies on that is on
fragile ground, isn't it ?

Why do you say that it's likely going to change ?

Because the internal implementation is going to change ?

Has it ever changed alrready, in the past ?

I think a macro stepper is a fundamental tool. Macros are a unique feature
(as far as I understand) of lispy languages and there are some notable
libraries implemented as macros

The ability to step through them would allow for tinkerers to explore

I suppose this is exactly the reason why Racket has its macro stepping and
inspecting tools

I'm a bit surprised that such a fundamental functionality seems to be an
afterthought in Guile


> Will Guile have anything equivalent ? I asked myself
> >
> > Now you show me the "(system syntax)" namespace (or module)
> >
> > I didn't  suspect it existed
> >
> > Does the manual mention it anywhere ? I didn' t see it
>
> Do you know how to search the manual or its index?  Press 'i' from
> either the Emacs or standalone info browsers to search the index, where
> you can find 'syntax-local-binding'.
>

But how would I have known that I should have looed for
"syntax-local-binding" ?

And why not any other name ?

I mean, the manual doesn't even mention macro stepping

You can also search the entire manual text by pressing 's'.  You can
> find (system syntax) that way.
>

Ok

But again: how would have I guessed that the namespace containing stuff I
was interested in was named (system syntax)" and not in any oher way ?

Of course I can search through a manual


> > Or maybe does it belong to any scheme standard ?
>
> No, certainly not.
>
> > Do any more (system ....) namespaces exist ?
> >
> > How would I know ?
>
> Look in the "module" subdirectory of the Guile source tree for modules
> that come with Guile itself, or more generally in the directories of
> %load-path after installation.  The directory structure mirrors the
> module namespaces.  The module (foo bar baz) is found in
> <DIR>/foo/bar/baz.scm, where <DIR> is a component of %load-path.  For
> example, (system syntax) is in <DIR>/system/syntax.scm.  In the Guile
> source tree, it's in module/system/syntax.scm.


Ok, I see those, now

 Thanks


^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2018-06-05  7:11 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2018-05-21 15:48 macroexpand-1 Catonano
2018-05-29 15:01 ` macroexpand-1 Mark H Weaver
2018-05-29 19:01   ` macroexpand-1 Catonano
2018-05-29 19:01     ` macroexpand-1 Catonano
2018-05-30  1:07     ` macroexpand-1 Mark H Weaver
2018-05-31  8:21       ` macroexpand-1 Catonano
2018-06-03 14:04         ` macroexpand-1 Mark H Weaver
2018-06-05  7:11           ` macroexpand-1 Catonano

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).