LLGPL Clarification

Bob Rogers rogers-acl@rgrjr.dyndns.org
Fri, 31 May 2002 22:12:09 -0400


   From: John Foderaro <jkf@franz.com>
   Date: Fri, 31 May 2002 13:53:50 -0700

   >> The
   >> prevailing notion of software copyright (in my non-professional
   >> understanding) is that object code implicitly carries the copyright of
   >> the source code from which it was compiled. 

   But you have to understand that Lisp macros are functions that
   operate in an internal form and return objects in an internal form.
   The purpose of a library of functions is to supply useful
   functionality.  A macro in a library is there to be used.  The idea
   that using a macro as it was designed to be used is somehow stealing
   its definition is something I find completly illogical.  There are an
   infinite number of illogical things you can propose and having a
   license agreement refute each of these would make for a very long
   license agreement.

Remember that we are not talking about logic, but about law; the
distinction is important.  ;-}

   Once you have read this entire message, please say again whether you
think this concern is illogical.

   You keep implying that everything derived from source is source.

I don't think I ever said this, but I am confused by your use of
"derived" here.  By (legal) definition, everything "derived" from source
falls under the copyright of the source, and we are wrestling with the
meaning of "derived" as it applies to macros.

   I wrote this macro

       (defun add2 (x y) `(+ ,x ,y))

   and you think that the macroexpansion

       (add2 1 2)  to    (+ 1 2)

   looks so much like the original source that the lisp system
   must have just copied the source code and done a cpp like
   textual substition.

Actually, the fact that you are showing the printed representation of
the macroexpansion and calling it "the macroexpansion" underscores how
little practical difference there is between the internal and external
"source" representation.  But of course, Lisp has always swept under the
rug the distinction between the pointer and the object pointed to, so
this way of speaking is only natural.

   But, again, I say this has no bearing on the discussion, because the
copyright notion of "derived work" is designed to cover *all* such
transformations.

   What if I wrote the macro like

       (defmacro add2 (x y) (list '+ x y))

   are you satisfied now that (add2 1 2) to (+ 1 2) is not
   recovering source from the original copyrighted code.

   If now, how about

      (defmacro add2 (x y) (cons '+ (cons x (cons y nil))))

   are we getting far enough away that you're satisifed that using
   the add2 macro isn't recovering actual source code?

This proves only that this is a bad example.  Really, the fact that  it
could be written in C as

	#define add2(x,y) ((x)+(y))

shows that nothing unique to Lisp is being captured here.

   If you think that this distiction between a lisp macro returning
   an internal representation and a lisp macro printing out a 
   lisp source and then reading it in is trivial, then I urge you 
   to try to write a large lisp program where macros expansions
   are always printed and then read in.   It won't be common lisp
   for sure and it won't even be a lisp in which you'll find very 
   macros very useful.  

I disagree with both assertions.  Over the past 20 years, I have written
some fairly hairy macros, and I've fixed some really broken ones that
others have written; it seems to me that the average Lisp programmer
doesn't have much understanding of the art of writing macros.  The
experience has taught me a few things:

   0.  Macros with side effects are extremely fragile, and should be
avoided at all costs.  I can't tell you how many hours I've spent
debugging these abortions.  If an expanded definition must record
something into a data structure, let the expansion do the work; it's
much cleaner that way.

   1.  It is better to push as much of the run-time semantics into
function calls, and reserve the macro expansion for pure syntactic
sugar.  (If syntactic sugar is not needed, then it should be written as
an ordinary fuction.)  This leads naturally to a contination-passing
style, which cuts down on the need to recompile after changes, among
other advantages.

   2.  Be very careful of constant structures in macros, especially
constant arrays, because sooner or later some expanded code is going to
mutate them (unless you can prevent the expansion from accessing them at
all).

As a result, my macros are usually small, with a few simple code
rearrangements that affect evaluation order.  And (with a few
exceptions) they would work in my hypothetical broken hacked compiler.
(Though I probably should have looked at code/macs.lisp from
http://rgrjr.dyndns.org/icancad/ICanCAD-0.2.1.tgz before spouting off.
At least this shows I have much confidence, if not much sense.)

   And most of these macros would be amply covered by the exemption in
section 5 of the original LGPL, if I were to release them under it.

   >>  Lisp macros generate something that is derived from
   >> the source of both the macro definition (macro boilerplate that is part
   >> of the Library code) and the macro call (the application code);

    In each of my three definitions of add2 show me the macro boilerplate
   to which you refer.  If there is no macro boilerplate do you
   believe that the code using that macro is not derived from
   the source code of the macro?

I'll believe that, but only because the example is trivial.  Lets look
at a more real-world LLGPL example, something from xmlutils/pxml1.cl
that is a bit larger, if only slightly more interesting:

    (defmacro next-char (tokenbuf read-sequence-func)
      `(let ((cur (tokenbuf-cur ,tokenbuf))
	     (tb (tokenbuf-data ,tokenbuf)))
	 (if* (>= cur (tokenbuf-max ,tokenbuf))
	    then				;; fill buffer
		 (if* (or (not (tokenbuf-stream ,tokenbuf))
			  (zerop (setf (tokenbuf-max ,tokenbuf)
				   (if* ,read-sequence-func
				      then (funcall ,read-sequence-func tb
						    (tokenbuf-stream ,tokenbuf))
				      else (read-sequence tb (tokenbuf-stream ,tokenbuf))))))
		    then (setq cur nil)	;; eof
		    else (setq cur 0)))
	 (if* cur
	    then (prog1
		     (let ((cc (schar tb cur)))
		       (if (and (tokenbuf-stream ,tokenbuf) (eq #\return cc)) #\newline cc))
		   (setf (tokenbuf-cur ,tokenbuf) (1+ cur))))))

The reason it's only slightly more interesting is that even this macro
could be expressed in C; it's doing pure token substitution.  (In fact,
there is no reason this should be a macro; it could more profitably be
coded as an inline-able function.  Of course, if next-char *had* been
defined as a function, there would be no issue.)  And when any function
that calls next-char is compiled, the resulting function object
*manifestly* contains binary code that is derived from the macro, just
as if the definition had been cut-and-pasted into the point of call.

   Or do you disagree?

   Of course, rewriting this macro as a blizzard of conses would greatly
obscure the relationship of the original source to the expansion,
perhaps even to the point of voiding any claim that the expansion was
"derived from" (rather than "produced by") the source.  But this is
unhelpful, as I've never seen anybody actually code this way.  In my
experience, the next-char example is close to being typical
(unfortunately).

					-- Bob Rogers