guile-devel
[Top][All Lists]
Advanced

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

Re: Syntax Parameters documentation for guile


From: Ian Price
Subject: Re: Syntax Parameters documentation for guile
Date: Tue, 03 Jan 2012 23:04:30 +0000
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/23.3 (gnu/linux)

Eli Barzilay <address@hidden> writes:

>> Syntax parameters[fn:1] are a mechanism for rebinding a macro
>> definition within the dynamic extent of a macro expansion. It
>> provides a convenient solution to one of the most common types of
>> unhygienic macro: those that introduce a special binding each time
>
> I'd explicitly say "unhygienic" here rather than "special".
Good point, I've changed it.

>> the macro is used. Examples include an 'if' form that binds the
>> result of the test to an 'it' binding, or class macros that
>> introduce a special 'self' binding.
>
> The `abort' example is also popular, probably even more than `it'.  I
> think that there are practical uses of that (eg, a function with a
> `return' keyword), whereas anaphoric conditionals are more of an
> academic exercise that I don't think gets used in practice (at least
> in Schemes).
I mentioned it mostly because it is a traditional example, but I've
swapped it for the lambda with return.

I'm not sure how popular aif is in general (I prefer a clojure style
if-let), but I do know it is in the popular miscmacros egg for chicken.

>> is preserved. This is similar to the dynamic binding mechanisms we
>> have at run-time like parameters[fn:2] or fluids[fn:3].
>
> An important note to add here is that there is no "dynamic scope" in
> the usual sense here -- it's rather a dynamic scope during macro
> expansion, and for macro-bound identifiers.  The resulting expanded
> code is of course as lexical as always.  (We've had some discussions
> at #scheme where this was a confusing point.)
Ok, I've cleared this up.

>> ** define-syntax-parameter keyword transformer [syntax]
>> Binds keyword to the value obtained by evaluating transformer as a
>> syntax-parameter.
>
> The keyword is bound to the value of the `transformer' expression.
> (Evaluated at the syntax level, in Racket's case, I don't know if
> Guile has separate phases yet...)  It's not evaluated as a syntax
> parameter, just like parameters.
This is a case of syntactic ambiguity in English, I meant it as 
"Binds keyword to (the value obtained by evaluating transformer) as a
syntax-parameter."
I've just dropped the "as a syntax-parameter" part.

>> The transformer provides the default expansion for the syntax
>> parameter, and in the absence of syntax-parameterize, is
>> functionally equivalent to define-syntax.
>
> A good note to add here is that it is usually bound to a transformer
> that throws a syntax error like "`foo' must be used inside a `bar'".
> It immediately clarifies the use of syntax parameters in the common
> case.
Done. I've also added an example that does this.

>> ** syntax-parameterize ((keyword transformer) ...) exp ... [syntax]
>> (note, each keyword must be bound to a syntax-parameter 
>> 
>> Adjusts each of the keywords to use the value obtained by evaluating
>> their respective transformer, in the expansion of the exp forms. It
>> differs from let-syntax, in that the binding is not shadowed, but
>> adjusted, and so uses of the keyword in the expansion of exp forms
>> use the new transformers.
>
> A possibly useful analogy is with `fluid-let' which doesn't create new
> bindings, but rather `set!'s them.  But IMO `fluid-let' should die, so
> using parameters is a better example...
I've made the comparison with regular parameters more explicit.

Thanks a lot, Eli.

In case anyone else has any comments, the modified version is included
below. Now I'm off to learn about texinfo :)

* Syntax Parameters

Syntax parameters[fn:1] are a mechanism for rebinding a macro
definition within the dynamic extent of a macro expansion. It provides
a convenient solution to one of the most common types of unhygienic
macro: those that introduce a unhygienic binding each time the macro
is used. Examples include a 'lambda' form with a 'return' keyword , or
class macros that introduce a special 'self' binding.

With syntax parameters, instead of introducing the binding
unhygienically each time, we instead create one binding for the
keyword, which we can then adjust later when we want the keyword to
have a different meaning. As no new bindings are introduced hygiene is
preserved. This is similar to the dynamic binding mechanisms we have
at run-time like parameters[fn:2] or fluids[fn:3], except that the
dynamic binding only occurs during macro expansion. The code after
macro expansion remains lexically scoped.

** define-syntax-parameter keyword transformer [syntax]
Binds keyword to the value obtained by evaluating transformer. The
transformer provides the default expansion for the syntax parameter,
and in the absence of syntax-parameterize, is functionally equivalent
to define-syntax. Usually, you will just want to have the transformer
throw a syntax error indicating that the keyword is supposed to be
used in conjunction with another macro. (see example)

** syntax-parameterize ((keyword transformer) ...) exp ... [syntax]
Adjusts each of the keywords to use the value obtained by evaluating
their respective transformer, in the expansion of the exp forms. Each
keyword must be bound to a syntax-parameter. syntax-parameterize
differs from let-syntax, in that the binding is not shadowed, but
adjusted, and so uses of the keyword in the expansion of exp forms use
the new transformers. This is somewhat similar to how parameterize
adjusts the values of regular parameters, rather than creating new
bindings.

** example

A short example, showing how you can use syntax parameters to create a
variant of 'lambda' that allows early return from the function via the
'return' keyword.

#+begin_src scheme
   (define-syntax-parameter return
     ;; by default we bind 'return' to a macro that merely raises a syntax error
     (lambda (stx)
       (syntax-violation 'return "return used outside of a lambda^" stx)))
  
  (define-syntax lambda^
    (syntax-rules ()
      [(lambda^ argument-list body bodies ...)
       (lambda argument-list
         (call-with-current-continuation
          (lambda (escape)
            ;; in the body we adjust the 'return' keyword so that calls
            ;; to 'return' are replaced with calls to the escape continuation
            (syntax-parameterize ([return (syntax-rules ()
                                            [(return vals (... ...))
                                             (escape vals (... ...))])])
              body
              bodies ...))))]))
  
  ;; now we can write functions that return early. Here, 'product' will
  ;; return immediately if it sees any 0 element.
  (define product
    (lambda^ (list)
      (fold (lambda (n o)
              (if (zero? n)
                  (return 0)
                  (* n o)))
            1
            list)))
  
#+end_src
* Footnotes
[fn:1] Eli Barzilay, Ryan Culpepper and Matthew Flatt. Keeping it
Clean with Syntax Parameters. In Proc. Workshop on Scheme and
Functional Programming, October 2011.

[fn:2] Marc Feeley. SRFI 39: Parameter objects, 2003.
http://srfi.schemers.org/srfi-39/.

[fn:3] 
https://www.gnu.org/software/guile/manual/guile.html#Fluids-and-Dynamic-States
-- 
Ian Price

"Programming is like pinball. The reward for doing it well is
the opportunity to do it again" - from "The Wizardy Compiled"



reply via email to

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