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? >>>>> >>>> >>>
