Arc Forumnew | comments | leaders | submitlogin
2 points by malisper 3805 days ago | link | parent

The idea behind autouniqs is that they remove some boilerplate code (ie w/uniq). The current implementation of autouniqs[0] is actually broken. The problem boils down to the fact that with the current implementation, macros will wind up using the same uniqs every time they are expanded. Say accum was implemented using autouniqs.

  (mac accum (accfn . body)
    `(withs (acc@ nil ,accfn [push _ acc@])
       ,@body
       (rev acc@)))
The intention is acc@ will be replaced with a uniq every time accum is expanded. With the current implementation of autouniqs, acc@ will be replaced with a single uniq in accum's definition. This leads to problems with nested use of accum.

  (accum a
    (accum b
      (...)))
What clojure does to solve the problem is combine "autogensyms" with its implementation of backquote. All the symbols ending in '#' inside of the same backquote are replaced with gensyms when the backquote is evaluated. While clojure's implementation is good for most things, it breaks down with any kind of nested backquotes [including `(... ,(... `(...)))] since the autogensyms will not be the same inside all of the backquotes. I think tying the autogensyms to backquote is a requirement due to the fact that every time the backquote is evaluated new gensyms would have to be put in to replace all of the autouniqs/gensyms (feel free to prove me wrong). This becomes obvious when one starts thinking about macros with nested backquotes and how they would expand. What I want is some version of autouniqs such that they completely eliminate the need for w/uniq.

[0] http://www.arclanguage.org/item?id=18235



2 points by akkartik 3804 days ago | link

Yeah, I actually thought about this use case back when I wrote accum for wart. But there's no problem here since the two expansions also create lexical scopes. In unmodified anarki:

  arc> (mac foo (accfn . body)
         `(withs (acc@ nil ,accfn [push _ acc@])
            ,@body
            (rev acc@)))
  #(tagged mac #<procedure: foo>)
  arc> (foo a
         (each l '(1 2 3)
           (a:foo b
             (each m (list l (+ l 1) (+ l 2))
               b.m))))
  ((1 2 3) (2 3 4) (3 4 5))
Is there some other scenario that I'm not considering?

(I hadn't realized that w/uniq is superior to implicit gensyms in this regard. Thanks.)

-----

2 points by malisper 3804 days ago | link

Okay, so the example with accum actually does work fine, but, only due to lexical scoping. I'm still afraid of the possibility of macros using the same symbols and causing a collision. While it looks like it would be an extremely rare bug to find, it would be one of those bugs that are a real pain to debug.

-----

2 points by akkartik 3804 days ago | link

Agreed. Which is why we should pool resources and share the first time we encounter such a bug. Or even a possible such bug that we can then debug together.

For my part, I have had an eye out for it for three years and have yet to encounter such a bug. Here's my hand-wavy reasoning for why I think it can't happen: for it to happen, an outer macro would have to rely on a use of a specific gensym inside a nested macro. I can't imagine how that could happen short of actually trying to use a gensym:

  (macro1
    (macro1
      ..x23..))  ; brittlely relying on the gensym turning into *x23*
And if we are to worry about this we can worry about all gensyms anyway.

-----