According to David Nolen, the macro chapter in the O'Reilly Clojure
Programming book is likely the best place to look – and what Toivoh gleaned
from the wikipedia page is the gist of it.


On Thu, Feb 20, 2014 at 7:36 AM, andrew cooke <[email protected]> wrote:

> there's a fairly high level description at
> http://my.safaribooksonline.com/book/programming/clojure/9781935182641/macros/ch08lev1sec5(needs
>  free trial subscription).  i'm not sure it has the details you want,
> though.  andrew
>
>
> On Thursday, 20 February 2014 07:08:08 UTC-3, Toivo Henningsson wrote:
>>
>> I've been perusing clojure.org, but was not able to find any real
>> documentation on how this works. The best that I found so far was from
>> http://en.wikipedia.org/wiki/Clojure#Macros:
>>
>> Clojure's macro system <http://en.wikipedia.org/wiki/Lisp_macro> is very
>> similar to that in Common Lisp <http://en.wikipedia.org/wiki/Common_Lisp> 
>> with
>> the exception that Clojure's version of the 
>> backquote<http://en.wikipedia.org/wiki/Grave_accent#Use_in_programming> 
>> (called
>> "syntax quote") qualifies symbols with their 
>> namespace<http://en.wikipedia.org/wiki/Namespace>.
>> This helps prevent unintended name capture, as binding to
>> namespace-qualified names is forbidden. It is possible to force a capturing
>> macro expansion, but this must be done explicitly. Clojure also disallows
>> rebinding global names in other namespaces that have been imported into the
>> current namespace.
>>
>> Does anyone know of where I might read up on how macros and macro hygiene
>> works in Clojure?
>>
>> On Thursday, 20 February 2014 05:57:07 UTC+1, Stefan Karpinski wrote:
>>>
>>> For what it's worth, I believe this is similar to how Clojure handles
>>> this as well.
>>>
>>>
>>> On Wed, Feb 19, 2014 at 2:43 PM, Toivo Henningsson <[email protected]>wrote:
>>>
>>>> I must say that I really like the idea of letting symbols keep track of
>>>> their context. In 
>>>> PatternDispatch.jl<https://github.com/toivoh/PatternDispatch.jl>,
>>>> the @pattern macro is really just a front end to the actual package
>>>> machinery. Since code generation is split across modules, I've had to do
>>>> all sorts of shenanigans to get the correct result while avoiding
>>>> fragility; mostly resorting to manually quoting in values instead of
>>>> leaving any symbols that would be internal, a symptom of which is this
>>>> line<https://github.com/toivoh/PatternDispatch.jl/blob/30c8d2cea9bfc181169bd7a35d4385a50264149c/src/Recode.jl#L12>
>>>> :
>>>>
>>>> const qemit!, qcalc!, qfinish!, qArg, qSource, qTupleRef, qCall, 
>>>> qInv,qBinding
>>>> , qEgalGuard, qTypeGuard, qgetfield, qtuple, qgetindex = map(quot, (
>>>> emit!, calc!, finish!, Arg, Source, TupleRef, Call, Inv, Binding,
>>>> EgalGuard, TypeGuard, getfield, tuple, getindex))
>>>>
>>>> To me, the natural context of a quote would just be the literal context
>>>> where it is found.
>>>>
>>>> When writing macros, I also find that it quite often is simpler to just
>>>> bypass esc altogether by wrapping the whole returned expression in an esc.
>>>> There is also the inconvenience that you often need esc in macros, while
>>>> eval pukes on it, which makes it hard to share code between expression
>>>> generation to be used for one and the other.
>>>>
>>>> +1 for symbols with context!
>>>>
>>>>
>>>> On Sunday, 9 February 2014 21:41:07 UTC+1, Jeff Bezanson wrote:
>>>>
>>>>> Hi David, this is a very good and interesting writeup. I will need
>>>>> some time to think about it.
>>>>> Your design might indeed be better; it is often hard to guess how
>>>>> these things will work out in practice.
>>>>>
>>>>> But at first glance, it does trade manual management of
>>>>> internal/external symbols using `esc` for sometimes-manual management
>>>>> of contexts. Macro writers would have to deal with things that they
>>>>> think of as symbols, but that are not symbols. This is exactly what I
>>>>> was trying to avoid. I doubt we're at a global optimum, but this
>>>>> tradeoff was chosen carefully.
>>>>>
>>>>>
>>>>> On Sun, Feb 9, 2014 at 3:16 PM, David Moon <[email protected]>
>>>>> wrote:
>>>>> > Hygienic macros could be both better and simpler. Julia's hygiene
>>>>> only works
>>>>> > in simple cases, I think, and requires too much manual intervention.
>>>>>  This
>>>>> > is because it is done in the wrong place, in the output of macro
>>>>> expansion,
>>>>> > so the macro expander has to guess the context of each symbol.
>>>>> Hygiene
>>>>> > ought to be done in the input to macro expansion, where the
>>>>> originating
>>>>> > context of every symbol is known.  The esc function is a dead
>>>>> give-away that
>>>>> > something is wrong.  It could be eliminated, which would make macros
>>>>> simpler
>>>>> > to define.  Errors like the one in the manual's sample definition of
>>>>> assert
>>>>> > would no longer occur.
>>>>> >
>>>>> > I believe Julia's macros are taken directly from Scheme.  Scheme
>>>>> never found
>>>>> > a fully satisfactory solution to the hygiene problem, because of the
>>>>> > inflexibility of S-expressions.  I have been thinking about this
>>>>> issue for
>>>>> > quite a few years.
>>>>> >
>>>>> > Fortunately the problem is easily solved in Julia, where the
>>>>> representation
>>>>> > of expressions is more flexible.  The key observation is that there
>>>>> should
>>>>> > be two kinds of symbols in the expansion of a macro, which I will
>>>>> call
>>>>> > "external" and "internal."  Internal symbols come from the macro
>>>>> definition,
>>>>> > external symbols come from the macro call.  Informally, external
>>>>> symbols are
>>>>> > visible to the caller of the macro and internal symbols are not,
>>>>> when used
>>>>> > as variable names.  Other uses for symbols, such as terminals in the
>>>>> grammar
>>>>> > ("else"), literals (":foo"), and field names (".x") do not
>>>>> distinguish
>>>>> > internal from external.
>>>>> >
>>>>> > More formally, when a variable name is looked up in a scope, an
>>>>> internal
>>>>> > symbol only matches the same internal symbol.  Thus binding an
>>>>> internal
>>>>> > symbol does not capture a reference to an external symbol, and vice
>>>>> versa.
>>>>> > When an internal symbol is not found in the current scope and its
>>>>> parents,
>>>>> > the external symbol with the same name is looked up in the scope
>>>>> where the
>>>>> > macro was defined.  Thus free references in a macro expansion will
>>>>> have the
>>>>> > intended meaning, being looked up in the macro definition scope when
>>>>> the
>>>>> > reference came from the macro, but in the macro call scope when the
>>>>> > reference came from the caller.
>>>>> >
>>>>> > How can this be implemented?  External symbols are the plain old
>>>>> symbols
>>>>> > that already exist.  Internal symbols are a new AST type with two
>>>>> fields,
>>>>> > name and context.  The name is the corresponding external symbol.
>>>>>  The
>>>>> > context remembers the scope where the macro was defined and is a
>>>>> unique
>>>>> > object freshly created for each macro call.  The quote construct
>>>>> converts
>>>>> > literal external symbols to internal symbols.  Interpolated data and
>>>>> literal
>>>>> > internal symbols are left alone.  The unary colon short form of
>>>>> quote is the
>>>>> > same.  Local variable names can be internal symbols and variable
>>>>> binding
>>>>> > lookup is adjusted as described above: two internal symbols only
>>>>> match if
>>>>> > both the names and the contexts are the same.  Uses of symbols other
>>>>> than as
>>>>> > variable names are modified to treat internal symbols the same as
>>>>> external.
>>>>> >
>>>>> > The esc function is no longer needed and should be removed.  Any
>>>>> expression
>>>>> > that originated in the macro call is automatically escaped.  Now the
>>>>> assert
>>>>> > example in the manual actually works.  If a macro like the zerox
>>>>> example in
>>>>> > the manual needs to put an external symbol into the expansion, it
>>>>> just uses
>>>>> > a plain old symbol:
>>>>> > macro zerox() :($(symbol("x")) = 0) end.
>>>>> >
>>>>> > When a global variable is defined in a module, if the name is an
>>>>> internal
>>>>> > symbol it is converted to an external symbol so it is generally
>>>>> visible.
>>>>> >
>>>>> > Where does the quote construct get the context when it makes an
>>>>> internal
>>>>> > symbol?  There are several ways it could be done.  I prefer for
>>>>> quote to use
>>>>> > the value of the variable context; if there is no variable with that
>>>>> name in
>>>>> > scope it is an error.  The macro statement implicitly defines the
>>>>> external
>>>>> > symbol context in the expander function.  Users who want to break
>>>>> parts of
>>>>> > the expander function into separate functions must pass the context
>>>>> around
>>>>> > explicitly.  Users who want to build expressions outside of a macro
>>>>> must
>>>>> > define context.  It may be useful to have a user-callable Context
>>>>> > constructor that takes a module as its argument.
>>>>> >
>>>>> > Because internal symbols are not interned, there may be a speed
>>>>> decrease in
>>>>> > some cases.  However this cost is only incurred at compile time.
>>>>> >
>>>>> > Macro-defining macros work, provided that when quote sees a literal
>>>>> internal
>>>>> > symbol it copies it unchanged into the expression being constructed.
>>>>>  Thus
>>>>> > the expansion of a macro defined by a macro-defining macro may
>>>>> contain
>>>>> > internal symbols whose context comes from either macro.  In the same
>>>>> way,
>>>>> > recursive or nested macros work, with each internal symbol
>>>>> remembering the
>>>>> > context where it originated.
>>>>> >
>>>>> > I have no strong opinion on whether macros are allowed to be defined
>>>>> in a
>>>>> > non-top-level context.  If not, the macro definition scope
>>>>> remembered in an
>>>>> > internal symbol's context is just a module.
>>>>> >
>>>>> > A literal symbol is no longer the same thing as construction of an
>>>>> > expression consisting of only a literal variable name.  The former
>>>>> produces
>>>>> > an external symbol, the latter produces an internal symbol.  One
>>>>> approach
>>>>> > would be to disallow :x for a literal symbol and require symbol("x")
>>>>> to be
>>>>> > used, but the verbosity might be unpopular.  Another approach would
>>>>> be to
>>>>> > treat :x as a special case; if you want to produce an internal
>>>>> symbol x you
>>>>> > must use quote x end or :(x).
>>>>> >
>>>>> > Incompatible changes here:
>>>>> > - Remove esc (or make it a no-op).
>>>>> > - quote no longer works if context is not defined.
>>>>> > - same for unary colon, unless the argument is just a symbol.
>>>>> >
>>>>> > Maybe the existing macros are good enough for your purposes, but I
>>>>> think the
>>>>> > hygiene could work better.  What do you think?
>>>>>
>>>>
>>>

Reply via email to