macro FAQ

Jacek Generowicz jacek.generowicz at cern.ch
Tue Aug 26 03:09:57 EDT 2003


hanzspam at yahoo.com.au (Hannu Kankaanpää) writes:

 
> if y > self.y - bs and y < self.y + bs + self.height:
>     return (abs(x - self.x) < bs,
>             abs(x - (self.x + self.width)) < bs)
> else:
>     return False, False
> 
> 
> and then
> 
> 
> if x > self.x - bs and x < self.x + bs + self.width:
>     return (abs(y - self.y) < bs,
>             abs(y - (self.y + self.height)) < bs)
> else:
>     return False, False
> 
> Obviously this was quite unsatisfactory. I ended up
> putting the axis code in a separate class

Do you mean "function" rather than "class" ?

> so I could
> use them interchangeably. I.e. If I passed
> func(self.y, self.x)
> and then
> func(self.x, self.y)
> 
> I would get the same effect on both axises. But this
> would've been an excellent place for macros IMO

I don't see what you gain by using a macro, wrt to using a function in 
_this_ case. As no Python macro system exists, I cannot form an 
opinion of what the two approaches would look like in Python, but in 
Lisp the calls to the macro would look just like the calls to the 
function, I think. The only difference would be in the definition of 
the macro/function, and the macro would be no simpler, so you wouldn't 
really gain anything. 
 
> Using macros 
> that combine both function call and "code block" syntax, 
> I could've written a simple function like this: 
>  
> defBoth getSize(self): 
>     return self.size 
>  
> And it would've been expanded to 
>  
> def getWidth(self): 
>     return self.width 
>  
> def getHeight(self): 
>     return self.height 
>  
> The macro would've had to change all "size" to either 
> "width" or "height", and also change "pos" to either "x" 
> or "y" and so on. 
 
So you would not only replace whole symbols, but even fragments of
symbols (getSize -> getHeight), and thus macically/implicitly create
new symbols. Many people consider this bad practice. The objection
being that readers of the code come across a symbol in the source, go
off and search for its definition, and tricks like this mean that they
end up wasting their time.

The defstruct macro does this sort of thing; (defstruct foo ... )
would "magically" define a constructor called "make-foo" (and other
stuff). IIRC, the experience with defstruct led the designers of CLOS
to explicitly avoid doing this in CLOS' defclass macro (but I'm far
from the world's leading authority on Lisp history).
 
Incidentally, in this case, having a string based code representation
would make your job much easier than the structured representation
which Lisp uses. In a string you'd merely do a search and replace; in
an s-expression you would have to recursively search for all symbols
in all sub-expressions, and then do the search and replace within the
name of each symbol you find. 

> This way, I could've got no duplicated code but 
> also a more intuitive interface than I currently have 
> (to get width, one needs to type obj.x.getSize() instead 
> of obj.getWidth()). And it's obvious this kind of "defBoth" 
> wouldn't be added as a language level construct -- Thus 
> macros are the only good solution. 
 
Cue metaclass solution ... :-)

(I suspect that even with metaclasses, you wouldn't be able to avoid
eval ... and that amounts to "informally" writing a macro)




More information about the Python-list mailing list