From: Johann Hibschman
Subject: how much macro is too much macro
Date: 
Message-ID: <mt7ly2u8rw.fsf@astron.berkeley.edu>
Hello, all.

I've been writing a few basic macros and thought I'd would fish for
some style pointers.  How far should I take this idea of
code-generation?  At what point does it become obfuscatory?

Also, is there an easy equivalent for generating functions, like in
Scheme (assuming compose had been defined appropriately):

  (define expsqrt (compose exp sqrt))

I've been using my macro "fsetq", below.  What are the pros/cons of
doing this?

The goal of this is to auto-generate some convenient functions for
dealing with vectors of double-floats, with the naming scheme that vv+
would elementwise add two vectors, vs+ add a vector to a scalar, sv+
add a scalar to a vector, etc.  I would eventually try to build a more
convenient structure on top of this somehow.

This is what I have so far:

(defun make-dvec (n)
  (make-array n :element-type 'double-float))

(defun make-unary-f (op)
  "vector functions"
  #'(lambda (xs)
      (let ((out (make-dvec (array-total-size xs))))
	(map-into out op xs))))

(defun make-binary-vv-f (op)
  "vector-vector functions"
  #'(lambda (xs ys)
      (let ((out (make-dvec (max (array-total-size xs)
				 (array-total-size ys)))))
	(map-into out op xs ys))))

(defun make-binary-vs-f (op)
  "vector-scalar functions"
  #'(lambda (xs s)
      (let ((out (make-dvec (array-total-size xs))))
	(map-into out #'(lambda (x) (funcall op x s))
		  xs))))

(defun make-binary-sv-f (op)
  "scalar-vector functions"
  #'(lambda (s xs)
      (let ((out (make-dvec (array-total-size xs))))
	(map-into out #'(lambda (x) (funcall op s x))
		  xs))))

(defmacro fsetq (function-name expression)
  `(setf (symbol-function ',function-name) ,expression))

(defmacro defbinary (op)
  (let* ((name (symbol-name op))
	 (vvname (concatenate 'string "VV" name))
	 (vsname (concatenate 'string "VS" name))
	 (svname (concatenate 'string "SV" name)))
    `(progn
       (fsetq ,(intern vvname) (make-binary-vv-f #',op))
       (fsetq ,(intern vsname) (make-binary-vs-f #',op))
       (fsetq ,(intern svname) (make-binary-sv-f #',op)))))

(defmacro defunary (op)
  (let* ((name  (symbol-name op))
	 (vname (concatenate 'string "V" name)))
    `(fsetq ,(intern vname) (make-unary-f #',op))))

;; create some functions

(defunary exp)
(defunary log)
(defunary sqrt)
(defunary cos)
(defunary sin)
(defunary tan)

(defbinary +)
(defbinary -)
(defbinary *)
(defbinary /)
(defbinary expt)

;; -- end --

Thanks for any pointers,

--Johann
From: Barry Margolin
Subject: Re: how much macro is too much macro
Date: 
Message-ID: <Rg9V1.31$eJ2.452604@burlma1-snr1.gtei.net>
In article <··············@astron.berkeley.edu>,
Johann Hibschman  <······@physics.berkeley.edu> wrote:
>I've been writing a few basic macros and thought I'd would fish for
>some style pointers.  How far should I take this idea of
>code-generation?  At what point does it become obfuscatory?

In general, any time you find yourself repeating something more than two or
three times, it's a good indication that you should write it once and give
it a name.  And the longer that repeated block is, the more appropriate it
is to extract it as a function or macro.  It's best if the units match the
abstractions in your application.  The examples in your post look very
good.

>Also, is there an easy equivalent for generating functions, like in
>Scheme (assuming compose had been defined appropriately):
>
>  (define expsqrt (compose exp sqrt))
>
>I've been using my macro "fsetq", below.  What are the pros/cons of
>doing this?

This is the way to do it.  The only real disadvantage is that some
programming environments attach additional identification information to
functions when they're defined using DEFUN.  E.g. if you use the above
definition, and then evaluate (symbol-function 'expsqrt), you'll probably
get something like #<FUNCTION 123456>.  But if you do:

(defun expsqrt (x)
  (exp (sqrt x)))

then you may get the more useful #<FUNCTION EXQSQRT 123456>.

This suggest that you might want to do something like:

(defmacro def-unary-f (name op)
  `(defun ,name (xs)
     (let ((out (make-dvec (array-total-size xs))))
       (map-into out ,op xs))))
(defmacro def-binary-vv-f (name op) ...)
(defmacro def-binary-vs-f (name op) ...)
(defmacro def-binary-sv-f (name op) ...)

(defun intern-concat (&rest strings)
  (intern (apply #'concatenate 'string strings)))

;; The distinction between DEF-UNARY-F and DEFUNARY is minor, but it
;; maintains a parallel between DEFUNARY and DEFBINARY.
(defmacro defunary (op)
  (let* ((name (symbol-name op))
	 (fname (intern-concat "V" name)))
    `(def-unary-f ,fname #',op)))

(defmacro defbinary (op)
  (let* ((name (symbol-name op))
	 (vvname (intern-concat "VV" name))
	 (vsname (intern-concat "VS" name))
	 (svname (intern-concat "SV" name)))
    `(progn
       (def-binary-vv-f ,vvname #',op)
       (def-binary-vs-f ,vsname #',op)
       (def-binary-sv-f ,svname #',op))))

-- 
Barry Margolin, ······@bbnplanet.com
GTE Internetworking, Powered by BBN, Burlington, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.