Arc Forumnew | comments | leaders | submitlogin
An arcish solution to name capture
3 points by lisper 5880 days ago | 10 comments
Since I've criticized Arc for not having hygienic macros I thought it incumbent on me to offer a solution within the spirit of the language. Here's what I've come up with.

1. Arrange for DEF to define not only the identifier specified, but also an additional identifer with a distinguished prefix character (I suggest using the carat (^) to evoke the notion of "top level") and associates both of those identifiers with the same binding.

2. Within macros, adopt the convention that whenever you want to invoke a top-level definition you invoke the carat-prefix version.

3. Never locally bind any identifier that begins with a carat.

So, for example:

(def my-frob (x) ...)

(mac my-macro (...) `(^my-frob ...))

Without the carat there is the danger that someone will do:

(let my-frob ... (my-macro ...))

which won't work. With the carat convention that won't happen unless someone deliberately circumvents it by binding an identifier with a carat prefix.



1 point by drcode 5879 days ago | link

I think this idea has some merit. However, I would argue it's still not arcish enough (but better than other ideas I've seen)

The basic issue I would have with this is that variable capture like this happens maybe in 1/1000 uses of macros (thought it's already happened once to me, so I agree with your concern is valid)

However, your solution would make 100% of macro definitions ungainly, which is too high a cost, IMHO.

The best solution I can think of is:

1. Use your carat idea, except don't waste a valuable non-alpha character. Instead have a function "only-top" that guarantees that a symbol used as a function refers only to a toplevel definition (which would work through a shadowed name at the definition point, though the name would be a collision-safe gensym mapped to the toplevel definition via a hash table- not a carat appended name)

2. Give a warning when arc detects a name collision with a local variable during macroexpansion

So if you write this:

  (def my-frob (x) ...)

  (mac my-macro (...) `(my-frob ...))

  (let my-frob ... (my-macro ...))
you would get an warning message:

  *Warning- expansion of my-macro causes collision of symbol MY-FROB*
You could remove the warning (if you don't want to change the local variable name) by changing the macro to:

  (mac my-macro (...) `(,(only-top 'my-frob) ...))
You would have some kind of similar method for hiding the error, in cases where the capture was actually desired.

With this solution, you could merily write macros without the cognitive load of an explicit name collision system, but still be safe from the problem when a rare name collision does occur.

-----

2 points by kennytilton 5878 days ago | link

"variable capture like this happens maybe in 1/1000 uses of macros (thought it's already happened once to me, so I agree with your concern is valid)"

How did it happen?! Did you skip a uniq out of laziness, or did the lexical environment into which you expanded rebind an Arc keyword or one of your own function names or ____? Maybe lisp-1 necessitates hygiene, and lisp-1 is The Real Problem.

As a longtime very relaxed CLer I must say I completely do not appreciate the naming paranoia I have experience with Arc/Lisp-1. Funcall is precisely what problem? My massive codebase shows 340 defmacros and 201 funcalls, which if you think about it is an astonishing ratio. (Think "define" vs. "invoke".)

-----

1 point by drcode 5878 days ago | link

I'm talking about function name collision, because it's a Lisp-1.

...I don't consider Lisp-1 a problem. It more than makes up for the rare name collision...

-----

2 points by lisper 5878 days ago | link

> The basic issue I would have with this is that variable capture like this happens maybe in 1/1000 uses of macros (thought it's already happened once to me, so I agree with your concern is valid)

Have you written 1000 macros? If not I'd say you underestimate the odds. Also, whatever the odds are, they just get worse as the size of the code base increases.

> However, your solution would make 100% of macro definitions ungainly, which is too high a cost, IMHO.

But macros only make up a small percentage of typical code, so this might still be OK.

> The best solution I can think of is:

Two problems with this:

1. Issuing those warnings is not easy. It requires a code walker. If you're going to go to that much trouble you might as well implement real hygienic macros.

2. (only-top foo) seems even more ungainly to me than ^foo.

I personally don't think the ^foo idea is the perfect solution, but given that PG doesn't like hygiene or Lisp-2 it seems like a good alternative. Better than burying your head in the sand and hoping the problem doesn't happen IMHO.

-----

1 point by almkglor 5879 days ago | link

There's just a problem: an error occurs on the macro user which must be fixed by the macro writer. Assuming the macro is complex enough, the user may not care to try hacking the macro source to get at the symbol to be (only-top ...)'ed.

Hmm. Problems, problems. My rename-the-local-variables solution won't work very well for macros which accept literal, unquoted symbols.

-----

2 points by drcode 5879 days ago | link

Understood, but if the user has the problem and doesn't want to hack on the macro, all they'd have to do is rename their local variable, which would be an OK comprise I think.

-----

1 point by lisper 5878 days ago | link

Until the day you want to do this:

(mac1 ... (mac2 ...

and find that MAC1 has a name conflict with MAC2, and both MAC1 and MAC2 were written by someone else.

-----

1 point by aidenn0 5879 days ago | link

What's wrong with just using gensym?

-----

1 point by drcode 5879 days ago | link

you can't for this specific problem, where functions are being called from an expanded macro and are shadowed by a local variable- look at the examples in the post.

-----

3 points by lisper 5878 days ago | link

There are two name capture problems. Gensym solves the other one. :-)

-----