Arc Forumnew | comments | leaders | submitlogin
4 points by kennytilton 6107 days ago | link | parent

Sorry (really) but I am afraid it's "just plain wrong". Variable capture is a feature, not a bug. It happens only if the macro author wants it to (on pretty rare occasions) or if they need to be fired for not using gensym to create an uninterned symbol which cannot possibly clash with another name/symbol.

As for a macro expanding into a larger lexical space where "list" has been bound to, say, 42, one of the joys of CL is never having to worry about that and we do it all the time (and its a bit of a shibboleth when we see people using "lst" in code they mention on comp.lang.lisp, we unleash the hounds who chase them over the fence to comp.lang.scheme where they belong).

I have written hundreds of macros including macro-generating macros over thirteen years and the only problem I ever has is figuring out nested backquotes, but that is because I just never managed to form a mental model of WTF is going on <g>, and it only takes me a couple of trials and errors to get past it.

There really is no problem with CL unhygienic macros. Oh, I should have mentioned that:

  (let ((list 42))
    (list 'hi 'mom))
... works fine and produces '(HI MOM) because CL is a Lisp-2 and shrugs off the variable name (in the operator position, variables do not apply (ugh, pun not intended). As for:

  (defun xxx ()
     (flet ((list (&rest args)
              (car args)))
        (list 'hi 'mom)))
...fine, here's another fine mess you've gotten me into (when I try to compile):

; While compiling XXX in C:\DOCUME~1\Kenny\LOCALS~1\Temp\cda9402357291.cl: Warning: Compiling a FUNCTION definition for the name LIST as a FLET. This name is in the COMMON-LISP package and defining it will be a violation for portable programs. The package COMMON-LISP has PACKAGE-DEFINITION-LOCK set, which causes the system to signal this violation.

The good news is that you are right about one thing, in CL we can use lib-x:same-name-func and lib-y:same-name-func to specify which we want should a clash arise.



3 points by KirinDave 6107 days ago | link

And a fine argument that is. The need for hygenic macros is overstated.

What I don't see is why you can't provide both? It's not confusing to provide both, it's not difficult to provide both. There are that class of macros you write where variable capture is tedious to deal with manually, so why not save some code and make a correct implementation of hygenic macros just so that people who need them don't have to re-implement them?

-----

3 points by kennytilton 6106 days ago | link

I just counted up again and my code base shows 613 defmacro occurences and only 111 gensym occurences and I do not even use a with-gensyms macro, so I am not sold on the need. Then again, I do strive for the functional thang and try not even to use let. btw, I am not fastidious generally but (I guess cuz it is so easy) I never skip on a gensym -- I am definitely of the school that believes programming is hard enough as it is. :)

-----

3 points by raymyers 6107 days ago | link

While we're on the subject, I know a number of people have been saying that Arc's unhygenic macros problems are a problem, because things like this break in a lisp-1:

    (mac break (a b) `(list ,a ,b))
    (let list nil (break 1 2))  =>  Error
However, you can simply unquote the function values in the macro.

    (mac dont-break (a b) `(,list ,a ,b))
    (let list nil (dont-break 1 2))  =>  (1 2)

-----

3 points by cadaver 6107 days ago | link

To make this work for functions, you need to add ((procedure? s) s) to 'ac. Otherwise with arc2.tar:

  arc> (mac dont-break (a b) `(,list ,a ,b))
  #3(tagged mac #<procedure>)
  arc> (let list nil (dont-break 1 2))
  Error: "Bad object in expression #<procedure: list>"
Macros still remain a problem:

  (mac list-macro parms `(,list ,@parms))
  (mac break (a b) `(,list-macro ,a ,b))
  (break 1 2) => Error

-----

2 points by raymyers 6107 days ago | link

I stand corrected, it worked in Anarki :)

And yeah, it obviously wouldn't work for macros, but macros don't get shadowed by let blocks in the first place.

    (mac list-macro args `(list ,@args))
    (mac dont-break (a b) `(list-macro ,a ,b))
    (let list-macro nil (dont-break 1 2))  =>  (1 2)

-----

1 point by cadaver 6107 days ago | link

Then it is I who shall stand corrected :)

-----

2 points by bogomipz 6107 days ago | link

A problem with unquoting the function value is that if you redefine the function, any code that uses the macro will hold on to the original function as a literal. This means the solution doesn't lend itself well to exploratory programming.

-----

2 points by raymyers 6107 days ago | link

Yes, it is true that preventing redefinition will in fact prevent redefinition. Bear that in mind when preventing redefinition :)

-----

1 point by bogomipz 6107 days ago | link

Well, one could create a module system so that the macro would always see the function the way it was seen in the module where the macro was defined. Then, when the function name is rebound where the macro is applied, it doesn't affect the macro, but if the function is recompiled in its original module, it does affect the code produced by the macro. I'm not saying it's straight forward, but I do believe it's possible.

-----

4 points by sacado 6107 days ago | link

"when we see people using "lst" in code they mention on comp.lang.lisp, we unleash the hounds who chase them over the fence to comp.lang.scheme where they belong" : be careful, it's not necessarily a schemer, it might also be pg, writing a function where both l and ls are already bound...

-----

4 points by kennytilton 6107 days ago | link

Excellent point, it might just be an Arc welder trying to save a char -- well, we checked with the hounds and they say a CL-style defmacro and (is nil #f) -> t don't mask the Lisp-1 scent, but they loved On Lisp and Ansi Common Lisp and ViaWeb being done in CL so pg is welcome any time.

-----

2 points by raymyers 6107 days ago | link

Well interestingly enough, li ls and lst don't appear in the non-Scheme source of Arc, except for lst in the borrowed mergesort.

What about xs and ys, do we get the hounds too?

-----

1 point by kennytilton 6107 days ago | link

Looks like my code when it's a three-liner and it really is a bit much to be coding (in my Algebra program):

  (loop for denominator in denominators...
You are right (guessing at the implicit): "list" is a terrible name for a variable unless someone really is writing a general purpose list manipulation function, but we do see "lst" quite a bit over on c.l.lisp.

btw, the real question is whether you see "list" as a variable name in the Scheme source.

-----

1 point by tel 6107 days ago | link

xs, ys, as, bs set off my Haskell alarm. They're pretty obvious and general, but something about pattern matching makes them unavoidable:

   interleve [] _ = []
   interleve _ [] = []
   interleve (x:xs) (y:ys) = x:y:(interleve xs ys)

-----