Arc Forumnew | comments | leaders | submitlogin
Unhygienic macros?
1 point by zunz 6120 days ago | 8 comments
I'm curious about Arc's current use of unhygienic macros.

Is this just a pragmatic 'it's not important yet' implementation decision which will be revisited later or a philosophical design objection to macro hygiene itself?

Admittedly, the hygienic macro algorithms are not simple (see Dybvig's portable version of syntax-case at http://www.cs.indiana.edu/chezscheme/syntax-case/) so in the interest of keeping Arc small for now, I can understand it.

But hygiene as a concept to avoid nasty macro suprises seems 'arc-ey' (for want of a better word) to me :-)

Note: I haven't delved into the arc sources yet.



1 point by tjr 6120 days ago | link

Looks like unhygienic macros was quite purposeful, not something being put off until later.

The issue with them is surprises with variable names; Arc addresses this by letting creating unique variable names for you in macros, if you request it. (See (uniq) in the tutorial.)

-----

2 points by zunz 6120 days ago | link

Thanks - manually generating unique variables where needed solves the arguably biggest part of the hygiene problem.

It's then part of macro writer discipline to remember to guard your introduced names. Fair enough. If you're writing macros, you arguably should know what you're doing.

It does still leave the problem of the macro using bindings from it's expansion point rather than it's definitition point i.e. you redefine if() and the macro uses that rather than the original definition and thus behaves badly.

Admittedly, redefining core parts of the language and using macros can be a 'don't do that then' sort of thing but considering arc's focus on language exploration, I would have thought having automatic guard-rails to be 'a good thing' (TM) (which is what hygiene really is IMO i.e. avoiding suprises from idea X interacting strangely with idea Y).

I've always felt the biggest benefit of hygiene is that it makes macro writing a bit more approachable for mere mortals. But considering the audience and arc's author(s), maybe that's not worth worrying about :-)

-----

1 point by tjr 6120 days ago | link

Yeah, it seems like, "let the programmer do whatever the want, no matter how silly it seems, because they might actually have a valid reason" drove a non-trivial amount of Arc design.

This sort of "programmer power" is similar to what you get in C. You can't redefine the language with C, but you can do some pretty nasty things if you want to. Or even if you make a subtle, honest mistake. Or even if you use C at all. ;-)

-----

1 point by kennytilton 6119 days ago | link

Mortals need not fear. Avoiding variable capture is easy albeit mildy tedious, and deliberate variable capture can be quite useful. I have seen good Schemers explaining why Scheme's macros were just as good as CL's and they convinced me they were not. :)

-----

1 point by petermichaux 6120 days ago | link

;;;;;;;;;;; the following doesn't work reliably ;;;;;;;;;;

;;; in Arc + is defined

;;; macro writers code depends on Arc's + ;;; but doesn't insure it always uses Arc's +

(mac foo () (list '+ 1 2))

;;; app writers code

(* 2 (foo)) ;;; 6

;;; app writer doesn't know foo depends on Arc's + ;;; and decides to redefine +

(= + (fn ((o x) (o y)) (prn "gotcha")))

(* 2 (foo)) ;;; error

;;;;;;;;;;;;;;;;;; the following works ;;;;;;;;;;;;;;;;;;;;

;;; in Arc + is defined

;;; macro writers code is tricky and ugly ;;; but will always use Arc's +

(= g (uniq))

(eval (list '= g '+))

(eval (list 'mac 'foo `() `(list (quote ,g) 1 2)))

;;; app writers code

(* 2 (foo)) ;;; 6

;;; redefining + doesn't affect the addtion function ;;; used in the foo macro

(= + (fn ((o x) (o y)) (prn "gotcha")))

(* 2 (foo)) ;;; 6

;;;;;;;;;;;; if foo is a function then it is easy ;;;;;;;;;;

;;; in Arc + is defined

;;; library writers code is easy because ;;; the original + can be in a closure

(= foo ((fn () (let plus + (fn () (plus 1 2))))))

;;; app writers code

(* 2 (foo)) ;;; 6

;;; redefining + doesn't affect the addtion function ;;; used in the foo function

(= + (fn ((o x) (o y)) (prn "gotcha")))

(* 2 (foo)) ;;; 6

-----

2 points by randallsquared 6120 days ago | link

It seems to me that a solution that preserves easy macros is to have the mac macro look for some syntax that is used to insert w/uniq calls.

-----

1 point by zunz 6120 days ago | link

Sure, you could indeed use markup to make the burden of uniq-ing lower - that mitigates the impact of the design decision, but doesn't address the underlying issue.

Fundamentally, hygienic macro systems assume that capture is unintentional by default (although you can ask for it), rather than assuming that capture is the default and you have to ask to avoid it.

When writing macros, which is the more common default? Since arc appears to be using macros extensively in it's implementation, perhaps it was found that capture was actually more common but that implies a language implementation issue has been 'promoted' to a language design issue. Maybe I shouldn't see those as seperate - this is LISP after all :-)

Hygiene is one of those things that doesn't bite you when the code-base (or more importantly, the idea-space) is small. But as the system gets bigger (and I'm assuming we all want arc to be good for big problems), you can't keep it all in your head anymore and spending many hours debugging a complex application to find a simple macro capture oversight bit you is not fun.

-----

1 point by noahlt 6120 days ago | link

PG wrote in Practical Common Lisp that variable capture, when used intentionally, can be useful.

-----