Arc Forumnew | comments | leaders | submitlogin
2 points by almkglor 4020 days ago | link | parent

By taking advantage of the relation:

  (fn (x) (expression x)) == (fn (y) (expression y))
Suppose that our hypothetical 'fn builtin automatically did this:

  (mac new-fn (parms . body)
    (let ptab (fill-table (table) (mappend [list _ (parms)] parms))
      (let translate
           (afn (xs)
              (if (caris xs 'quote)
                 (map [if (acons _) (self _) (or (ptab _) _)] xs)))
        `(fn ,@(translate (cons parms body)))))
This solves two problems currently in Arc: local variables can override function names used by macros, and macro names always override local variables.

1 point by ctdean 4020 days ago | link

Unfortunately I have no idea what that macro is supposed to do since it won't run for me. Perhaps you could post a different version?


1 point by almkglor 4020 days ago | link

Sorry, not on my hacking computer right now, so I'm not sure. Will check later what the problem is, in the meantime, what's the error?


2 points by almkglor 4020 days ago | link

Aw nuts, missing parenthesis, then realized that Arc parameter forms aren't straightforward lists but have a nasty (o ...) syntax. Here's a more complex form:

   (mac new-fn (parms . body)
    (let ptab
      (fill-table (table)
        (mappend [list _ (uniq)]
          (accum add
              (optionalp [caris _ 'o]
               optional-var cadr
               (afn (x)
                 (if (optionalp x)         (add (optional-var x))
                     (acons x)             (do (self (car x)) (self (cdr x)))
                     (and x (isa x 'sym))  (add x))))
              (traverser parms)))))
      (let translate
           (afn (xs)
              (if (caris xs 'quote)
                 (map [if (acons _) (self _) (or (ptab _) _)] xs)))
        `(fn ,@(translate (cons parms body))))))


2 points by ctdean 4019 days ago | link

Now I see -- that's great, thanks. (It was the missing UNIQ that got me, I can add parens :))

I think that if we kept going down this direction we would eventually come up with a macro system that was "hygienic in practice". Building a good and simple hygienic macro system is a very achievable goal.

All I really want to say is that there isn't a trivial silver bullet solution: A simple module system doesn't fix name collision. A simple code walker doesn't fix it (it needs integration with the environment.)

I eagerly await the version of Arc that has a macro system where name collision can be avoided.


1 point by ctdean 4019 days ago | link

BTW, some comments on the code walker solution:

These are very hard to get right without hooks into the Lisp environment (at least they are for me!). For example, we always uniqify the variable names, but sometimes that doesn't do what we want. Consider:

  (mac my-assert (val)
    `(unless ,val
       (prn "Assertion failed for " ',val)))

  (new-let wallet 'no-money
    (prn "wallet => " wallet)
    (my-assert (positive wallet))
Where NEW-LET is defined using your NEW-FN. This gives "Assertion failed for (positive gs1449)" when I want "Assertion failed for (positive wallet)".

Also, I don't know if Arc has dynamic variables or not, but if if does those should also not be uniqified.

I'm going to exit this thread now, thanks for the code.


1 point by almkglor 4019 days ago | link

On assertions: it would really be nice to have at least just one extra namespace for local variables, I suppose, with printnames that are the same as the global namespace. new-fn then translates from symbols in the global namespace (which is what read should always emit) to local variable namespace. Aw crick, it's a hard problem.

Also, current bug with this implementation: (fn (do) `(do ,do)) will fail, I'll need to analyze do. And stuff like (fn (b) (tag b (prn "foo"))).