Re: LLGPL Clarification
Bob Rogers <[email protected]> Fri, 31 May 2002 22:12:09 -0400
| Newsgroups | gmane.lisp.open-source.franz |
|---|---|
| Message-ID | <[email protected]> |
From: John Foderaro <[email protected]> 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