As first, good luck with this list!
I'm in search for best examples of "code is data" paradigm in Common Lisp. For most CL-ers, it probably means "macros", but eval, backquote, anything that processes the code as data is of interest. As "best" I think on the most surprising, powerful, sophisticated examples, not necessarily of a pedagogical value.
Imagine that someone invited you to write the presentation "Five best CL macros ... I seen" or "Five best CL macros ... I wrote." What would you chose and why?
Kazimir Majorinc
My best macro so far ;-)
(defmacro access-slot (object &rest slot-names) `(ff:fslot-value-typed (ff:foreign-pointer-type ,object) :c (ff:foreign-pointer-address ,object) ,@slot-names))
(defmacro define-struct-getter (struct package &rest slots) (loop for s in slots do (let ((acc (intern (concatenate 'string (symbol-name struct) "-" (symbol-name s)))) (sp (intern s package))) (eval `(defmacro ,acc (object) `(access-slot ,object ',',sp))))))
because it is a macro which writes macros which uses macro. My first real world macro.
On Sun, Sep 5, 2010 at 4:24 PM, Kazimir Majorinc kazimir@chem.pmf.hrwrote:
As first, good luck with this list!
I'm in search for best examples of "code is data" paradigm in Common Lisp. For most CL-ers, it probably means "macros", but eval, backquote, anything that processes the code as data is of interest. As "best" I think on the most surprising, powerful, sophisticated examples, not necessarily of a pedagogical value.
Imagine that someone invited you to write the presentation "Five best CL macros ... I seen" or "Five best CL macros ... I wrote." What would you chose and why?
Kazimir Majorinc
pro mailing list pro@common-lisp.net http://common-lisp.net/cgi-bin/mailman/listinfo/pro
On Sun, Sep 5, 2010 at 11:56 AM, Yakov Zaytsev zaytsev.yakov@gmail.comwrote:
(defmacro define-struct-getter (struct package &rest slots) (loop for s in slots do (let ((acc (intern (concatenate 'string (symbol-name struct) "-" (symbol-name s)))) (sp (intern s package))) (eval `(defmacro ,acc (object) `(access-slot ,object ',',sp))))))
Rather than eval-ing the consed defmacro form at macroexpansion time, which doesn't do the right thing when a DEFINE-STRUCT-GETTER form is in a file being compiled, it would be better to do something like this:
(defmacro define-struct-getter (struct package &rest slots) `(progn ,@(mapcar (lambda (s) (let ((acc (intern (concatenate 'string (symbol-name struct) "-" (symbol-name s)))) (sp (intern s package)) `(defmacro ,acc (object) `(access-slot object ',',sp)))) slots)))
As tempting as it is to call EVAL for various purposes, the reality is that correct uses of EVAL are quite rare. In fact, unless you're writing your own read-eval-print loop of some kind, the best rule of thumb is that if you're calling EVAL explicitly, you've made a mistake. I have trouble coming up with any exceptions to this rule other than a REPL.
Also, supplying a package to re-intern the slot names in, while not outright wrong, is an unusual kind of thing to do. Is it that hard to just type the slot names with package prefixes?
All that said -- if you're using nested backquotes successfully, you're clearly starting to get the hang of macros.
-- Scott
On Mon, Sep 6, 2010 at 12:59 AM, Scott L. Burson Scott@sympoiesis.comwrote:
On Sun, Sep 5, 2010 at 11:56 AM, Yakov Zaytsev zaytsev.yakov@gmail.comwrote:
(defmacro define-struct-getter (struct package &rest slots) (loop for s in slots do (let ((acc (intern (concatenate 'string (symbol-name struct) "-" (symbol-name s)))) (sp (intern s package))) (eval `(defmacro ,acc (object) `(access-slot ,object ',',sp))))))
Rather than eval-ing the consed defmacro form at macroexpansion time, which doesn't do the right thing when a DEFINE-STRUCT-GETTER form is in a file being compiled, it would be better to do something like this:
(defmacro define-struct-getter (struct package &rest slots) `(progn ,@(mapcar (lambda (s)
(let ((acc (intern (concatenate 'string (symbol-name struct) "-"
(symbol-name s)))) (sp (intern s package)) `(defmacro ,acc (object) `(access-slot object ',',sp)))) slots)))
As tempting as it is to call EVAL for various purposes, the reality is that correct uses of EVAL are quite rare. In fact, unless you're writing your own read-eval-print loop of some kind, the best rule of thumb is that if you're calling EVAL explicitly, you've made a mistake. I have trouble coming up with any exceptions to this rule other than a REPL.
Also, supplying a package to re-intern the slot names in, while not outright wrong, is an unusual kind of thing to do. Is it that hard to just type the slot names with package prefixes?
All that said -- if you're using nested backquotes successfully, you're clearly starting to get the hang of macros.
-- Scott
re-interning the slot names saves typing because DEFINE-STRUCT-GETTERS form is used for structures which are generated by swig. anyhow it is first time I use INTERN e.g.
(define-struct-getter image :MagickWand columns rows)
and thank you for pointing my EVAL usage mistake!
On Sun, Sep 5, 2010 at 9:59 PM, Scott L. Burson Scott@sympoiesis.com wrote:
As tempting as it is to call EVAL for various purposes, the reality is that correct uses of EVAL are quite rare. In fact, unless you're writing your own read-eval-print loop of some kind, the best rule of thumb is that if you're calling EVAL explicitly, you've made a mistake. I have trouble coming up with any exceptions to this rule other than a REPL.
One of the many tricks I've picked up from James Bielman (I hope he's subscribed to this mailing list :-)) was using CONSTANTP and EVAL in compiler macros. Here's a simple example:
(defun plus (x y) (+ x y))
(define-compiler-macro plus (&whole form x y) (if (and (constantp x) (constantp y)) (+ (eval x) (eval y)) form))
Execution examples:
(compiler-macroexpand-1 '(plus 1 1)) => 2
(compiler-macroexpand-1 '(plus '1 '1)) => 3
(compiler-macroexpand-1 '(plus (* 2 (/ 4 2)) (+ 3 2))) => 9
(defconstant +1+ 1) (compiler-macroexpand-1 '(plus +1+ 2)) => 3
On Sun, Sep 5, 2010 at 3:56 PM, Luís Oliveira luismbo@gmail.com wrote:
One of the many tricks I've picked up from James Bielman (I hope he's subscribed to this mailing list :-)) was using CONSTANTP and EVAL in compiler macros.
Okay, it is true that anything that CONSTANTP says "true" on can fairly be EVALled. But be aware that implementations are not required to recognize something like '(* 2 (/ 4 2)) as a constant. (Maybe most of them do.)
-- Scott
Here's a simple example:
(defun plus (x y) (+ x y))
(define-compiler-macro plus (&whole form x y) (if (and (constantp x) (constantp y)) (+ (eval x) (eval y)) form))
Execution examples:
(compiler-macroexpand-1 '(plus 1 1)) => 2
(compiler-macroexpand-1 '(plus '1 '1)) => 3
(compiler-macroexpand-1 '(plus (* 2 (/ 4 2)) (+ 3 2))) => 9
(defconstant +1+ 1) (compiler-macroexpand-1 '(plus +1+ 2)) => 3
-- Luís Oliveira http://r42.eu/~luis/
pro mailing list pro@common-lisp.net http://common-lisp.net/cgi-bin/mailman/listinfo/pro
What happens if you do
(defun foo (x) (plus x 3))
?
Luís Oliveira wrote:
On Sun, Sep 5, 2010 at 9:59 PM, Scott L. Burson Scott@sympoiesis.com wrote:
As tempting as it is to call EVAL for various purposes, the reality is that correct uses of EVAL are quite rare. In fact, unless you're writing your own read-eval-print loop of some kind, the best rule of thumb is that if you're calling EVAL explicitly, you've made a mistake. I have trouble coming up with any exceptions to this rule other than a REPL.
One of the many tricks I've picked up from James Bielman (I hope he's subscribed to this mailing list :-)) was using CONSTANTP and EVAL in compiler macros. Here's a simple example:
(defun plus (x y) (+ x y)) (define-compiler-macro plus (&whole form x y) (if (and (constantp x) (constantp y)) (+ (eval x) (eval y)) form))
Execution examples:
(compiler-macroexpand-1 '(plus 1 1)) => 2 (compiler-macroexpand-1 '(plus '1 '1)) => 3 (compiler-macroexpand-1 '(plus (* 2 (/ 4 2)) (+ 3 2))) => 9 (defconstant +1+ 1) (compiler-macroexpand-1 '(plus +1+ 2)) => 3
On Tue, Sep 7, 2010 at 11:15 PM, Daniel Weinreb dlw@itasoftware.com wrote:
What happens if you do
(defun foo (x) (plus x 3))
Since (constantp 'x) yields nil, the compiler macro will bail out. I suppose a smarter version could perform some optimizations when only one of the arguments is constant.
On a related note, I just came across this SBCL effort https://bugs.launchpad.net/sbcl/+bug/632368 which has the potential to make compiler macros significantly more powerful by handing forms over only after they've been through the compiler's inlining and constant propagation phases!
Luís Oliveira wrote:
On Tue, Sep 7, 2010 at 11:15 PM, Daniel Weinreb dlw@itasoftware.com wrote:
What happens if you do
(defun foo (x) (plus x 3))
Have you tried it? CCL gets very unhappy!
Since (constantp 'x) yields nil, the compiler macro will bail out.
Yeah, you'd think.
Maybe I made a mistake.
I suppose a smarter version could perform some optimizations when only one of the arguments is constant.
On a related note, I just came across this SBCL effort https://bugs.launchpad.net/sbcl/+bug/632368 which has the potential to make compiler macros significantly more powerful by handing forms over only after they've been through the compiler's inlining and constant propagation phases!
On Thu, Sep 9, 2010 at 3:42 PM, Daniel Weinreb dlw@itasoftware.com wrote:
Have you tried it? CCL gets very unhappy!
WFM, yes.
OK, sorry, I must have made a mistake. -- Dan
Luís Oliveira wrote:
On Thu, Sep 9, 2010 at 3:42 PM, Daniel Weinreb dlw@itasoftware.com wrote:
Have you tried it? CCL gets very unhappy!
WFM, yes.
It's very, very hard to know what "best macro" means. It's like asking for the "best function". Perhaps what people mean is the "cutest" or "most remarkable" macro.
I'm rather proud of the macros I wrote for the logging system in QRes. I really ought to refactor it out and open-source it. What I wanted to do was to encourage people to add logging forms to their code. One reason people resist putting them in is that they worry about the time (compute) overhead when logging is turned off. So I thought to myself, what's the lowest we could get it to be? Well, suppose that every logging statement turns into a read of a special variable and a check to see whether it's NIL or not? You can't do a whole lot better than that, I'd think. Now, let's say you have hierarchical names of categories. You turn on logging for "foo.bar", and you do not want calls that log to "foo.quux" to get turned on; they too should be very, very fast. But you do not know in advance the names of all the logging categories there might be. I guess I could have required the programmer to add the categories to a file, but ANYTHING that makes logging harder will discourage people from adding logging statements. If you try to do this, you'll find that it's a bit tricky. You need some eval-when stuff. You need to create symbols dynamically. It's not rocket science, but it's sort of cool and it might be useful to others.
-- Dan
Yakov Zaytsev wrote:
My best macro so far ;-)
(defmacro access-slot (object &rest slot-names) `(ff:fslot-value-typed (ff:foreign-pointer-type ,object) :c (ff:foreign-pointer-address ,object) ,@slot-names))
(defmacro define-struct-getter (struct package &rest slots) (loop for s in slots do (let ((acc (intern (concatenate 'string (symbol-name struct) "-" (symbol-name s)))) (sp (intern s package))) (eval `(defmacro ,acc (object) `(access-slot ,object ',',sp))))))
because it is a macro which writes macros which uses macro. My first real world macro.
On Sun, Sep 5, 2010 at 4:24 PM, Kazimir Majorinc <kazimir@chem.pmf.hr mailto:kazimir@chem.pmf.hr> wrote:
As first, good luck with this list! I'm in search for best examples of "code is data" paradigm in Common Lisp. For most CL-ers, it probably means "macros", but eval, backquote, anything that processes the code as data is of interest. As "best" I think on the most surprising, powerful, sophisticated examples, not necessarily of a pedagogical value. Imagine that someone invited you to write the presentation "Five best CL macros ... I seen" or "Five best CL macros ... I wrote." What would you chose and why? Kazimir Majorinc _______________________________________________ pro mailing list pro@common-lisp.net <mailto:pro@common-lisp.net> http://common-lisp.net/cgi-bin/mailman/listinfo/pro
pro mailing list pro@common-lisp.net http://common-lisp.net/cgi-bin/mailman/listinfo/pro
On 7 Sep 2010, at 23:41, Daniel Weinreb wrote:
It's very, very hard to know what "best macro" means. It's like asking for the "best function". Perhaps what people mean is the "cutest" or "most remarkable" macro.
I was rather amazed when discovering a technique for integrating hygiene-compatible macros into Common Lisp. How to do this is described here: http://www.jucs.org/jucs_16_2/embedding_hygiene_compatible_macros (In the paper, it's described in terms of Scheme, because that's easier to explain, but the 'actual' implementation is in Common Lisp, as also described more superficially in that paper, and also available for download).
I'm not sure that this is actually a practically useful approach, but it surely shows how powerful Common Lisp macros are. (Definitely more powerful than many people think.) The technique in that paper could maybe become more practically useful if it gets integrated directly in a Common Lisp implementation itself. Not that I think that macro hygiene is actually a real problem... ;)
Pascal
Pascal Costanza wrote:
On 7 Sep 2010, at 23:41, Daniel Weinreb wrote:
It's very, very hard to know what "best macro" means. It's like asking for the "best function". Perhaps what people mean is the "cutest" or "most remarkable" macro.
I was rather amazed when discovering a technique for integrating hygiene-compatible macros into Common Lisp. How to do this is described here: http://www.jucs.org/jucs_16_2/embedding_hygiene_compatible_macros (In the paper, it's described in terms of Scheme, because that's easier to explain, but the 'actual' implementation is in Common Lisp, as also described more superficially in that paper, and also available for download).
I'm not sure that this is actually a practically useful approach, but it surely shows how powerful Common Lisp macros are. (Definitely more powerful than many people think.)
It would be wonderful if, in your copious free time, you could write a paper that expressed this: how powerful Lisp macros are. It would not need to be at a "peer-reviewed" sort of level; in fact, it would ideally be written so that a Python programmer could understand it.
I tried to write a Python vs. Common Lisp comparison once, but it was too hard and I didn't have enough time. The big question is, should we look at the existing languages like Python and Ruby that have co-opted so many of the good features of Lisp, and declare victory and go home. Well, Python isn't so good at using functions as objects (I read somewhere that Guido actually wanted to remove "map" and "reduce" and such from the language, but got too much pushback). Ruby, I gather, does that a lot more.
But the real point is that you can do so much more with macros, in all Lisps (including Clojure). I often get told, oh, you don't need Lisp macros; facilities in other languages can do all the things Lisp macros can do.
Although this is wrong, there isn't any quick way to answer the objection. So it would be nice if there were an easy-to-deploy explanation of why this is not true and Lisp macros really are a unique abstraction mechanism that's very valuable.
Unfortunately, writing this isn't easy, and all the people who know how to do it as very busy...
-- Dan
The technique in that paper could maybe become more practically useful if it gets integrated directly in a Common Lisp implementation itself. Not that I think that macro hygiene is actually a real problem... ;)
Pascal
On Wed, Sep 22, 2010 at 11:10 AM, Daniel Weinreb dlw@itasoftware.comwrote:
I tried to write a Python vs. Common Lisp comparison once, but it was too hard and I didn't have enough time.
[snip]
Peter Norvig wrote an introduction to Python for Lisp programmers that contains a comparison. While written from "the other direction", it could serve as a jumping off point:
http://norvig.com/python-lisp.html
-tree
Yes, I know about that. Thanks.
Tom Emerson wrote:
On Wed, Sep 22, 2010 at 11:10 AM, Daniel Weinreb <dlw@itasoftware.com mailto:dlw@itasoftware.com> wrote:
I tried to write a Python vs. Common Lisp comparison once, but it was too hard and I didn't have enough time.
[snip]
Peter Norvig wrote an introduction to Python for Lisp programmers that contains a comparison. While written from "the other direction", it could serve as a jumping off point:
http://norvig.com/python-lisp.html
-tree
-- Tom Emerson tremerson@gmail.com mailto:tremerson@gmail.com http://treerex.blogspot.com/
There is an old saying: if you are using "eval", you are doing it wrong. So far I have yet to find any significant exceptions to this rule.
I do not see what the eval is for. Just get rid of it and have the definition of define-struct-getter expand into the defmacro of the concatenated name.
Meanwhile, there are several problems here.
(1) In define-struct-getter, are the slots intended to be symbols, or forms that evaluate to symbols? If they are symbols, intern fails. If they are forms that are evaluated to symbols, symbol-name fails because it is called on the form. I could not get any case to work.
(2) In access-slot, you expand "object" twice, which can cause trouble if the form to which "object" is bound has side-effects. It is normal good practice to use generated symbols here.
(3) Package arguments are usually a pain in the neck. Standard practice is to just use "intern" so that it's interned in the current package.
-- Dan
Yakov Zaytsev wrote:
My best macro so far ;-)
(defmacro access-slot (object &rest slot-names) `(ff:fslot-value-typed (ff:foreign-pointer-type ,object) :c (ff:foreign-pointer-address ,object) ,@slot-names))
(defmacro define-struct-getter (struct package &rest slots) (loop for s in slots do (let ((acc (intern (concatenate 'string (symbol-name struct) "-" (symbol-name s)))) (sp (intern s package))) (eval `(defmacro ,acc (object) `(access-slot ,object ',',sp))))))
because it is a macro which writes macros which uses macro. My first real world macro.
On Sun, Sep 5, 2010 at 4:24 PM, Kazimir Majorinc <kazimir@chem.pmf.hr mailto:kazimir@chem.pmf.hr> wrote:
As first, good luck with this list! I'm in search for best examples of "code is data" paradigm in Common Lisp. For most CL-ers, it probably means "macros", but eval, backquote, anything that processes the code as data is of interest. As "best" I think on the most surprising, powerful, sophisticated examples, not necessarily of a pedagogical value. Imagine that someone invited you to write the presentation "Five best CL macros ... I seen" or "Five best CL macros ... I wrote." What would you chose and why? Kazimir Majorinc _______________________________________________ pro mailing list pro@common-lisp.net <mailto:pro@common-lisp.net> http://common-lisp.net/cgi-bin/mailman/listinfo/pro
pro mailing list pro@common-lisp.net http://common-lisp.net/cgi-bin/mailman/listinfo/pro
On 8.9.2010 0:08, Daniel Weinreb wrote:
There is an old saying: if you are using "eval", you are doing it wrong. So far I have yet to find any significant exceptions to this rule.
Look at this:
The program generates propositional formulas S1, S2, ... using some experimental algorithm. S1, ..., Sn are needed for generation of Sn+1, as typical in deductive systems. The formulas contain logical constants and already defined operators. The hypothesis is: all generated propositional expressions are true.
The problem: write the function that tests that hypothesis for any generated formula. Optimization is not needed. Simpler is better.
I'd use eval instead of defining any new function, as the simplest and the most natural solution. What would others do? If you'd use eval as well, do you think it is important or rare, maybe even 'artificial' example.
On Sep 14, 2010, at 1:07 PM, Kazimir Majorinc wrote:
On 8.9.2010 0:08, Daniel Weinreb wrote:
There is an old saying: if you are using "eval", you are doing it wrong. So far I have yet to find any significant exceptions to this rule.
Look at this:
The program generates propositional formulas S1, S2, ... using some experimental algorithm. S1, ..., Sn are needed for generation of Sn+1, as typical in deductive systems. The formulas contain logical constants and already defined operators. The hypothesis is: all generated propositional expressions are true.
The problem: write the function that tests that hypothesis for any generated formula. Optimization is not needed. Simpler is better.
I'd use eval instead of defining any new function, as the simplest and the most natural solution. What would others do? If you'd use eval as well, do you think it is important or rare, maybe even 'artificial' example.
This is a place you could use 'eval', but... - It depends on what the propositions contain. If it's a tiny subset of Lisp (like 'and', 'or', 'not'), a custom evaluator might be nicer. - A custom evaluator might be faster, too, especially if you're doing this in the context of some sort of SAT-solver where you've got lots of propositions, and you might want caching, etc.
In the many years I've been using Lisp, I've used eval so few times, and one of the times was in a debugger where I wanted to evaluate Lisp forms. :-)
Scott McKay wrote:
On Sep 14, 2010, at 1:07 PM, Kazimir Majorinc wrote:
On 8.9.2010 0:08, Daniel Weinreb wrote:
There is an old saying: if you are using "eval", you are doing it wrong. So far I have yet to find any significant exceptions to this rule.
Look at this:
The program generates propositional formulas S1, S2, ... using some experimental algorithm. S1, ..., Sn are needed for generation of Sn+1, as typical in deductive systems. The formulas contain logical constants and already defined operators. The hypothesis is: all generated propositional expressions are true.
The problem: write the function that tests that hypothesis for any generated formula. Optimization is not needed. Simpler is better.
I'd use eval instead of defining any new function, as the simplest and the most natural solution. What would others do? If you'd use eval as well, do you think it is important or rare, maybe even 'artificial' example.
This is a place you could use 'eval', but...
- It depends on what the propositions contain. If it's a tiny subset of Lisp (like 'and', 'or', 'not'), a custom evaluator might be nicer.
- A custom evaluator might be faster, too, especially if you're doing this in the context of some sort of SAT-solver where you've got lots of propositions, and you might want caching, etc.
Yes, I was just about to hit reply and say all these things, but Scott got there first.
I'll add that if you can make the tiny subset have no side effects, there are lots of good properties of that.
And if you can make it declarative, that has big advantages, although that might provide not enough power, depending on what you're planning to use this for.
-- Dan
In the many years I've been using Lisp, I've used eval so few times, and one of the times was in a debugger where I wanted to evaluate Lisp forms. :-)
pro mailing list pro@common-lisp.net http://common-lisp.net/cgi-bin/mailman/listinfo/pro
OK, but both of you paid more attention to the additional niceties or advantages, unspecified in the problem, than to confirm or reject the claim that eval is the best solution for problem under specified criteria "simpler is better."
It looks to me as implicit claim "Yes, eval is the best solution of that problem, but if the problem is developed further, other approaches (like few mentioned) will (soon?) prevail."
Is it right?
Anyone else?
------------------------------ Kazimir Majorinc:
Look at this:
The program generates propositional formulas S1, S2, ... using some experimental algorithm. S1, ..., Sn are needed for generation of Sn+1, as typical in deductive systems. The formulas contain logical constants and already defined operators. The hypothesis is: all generated propositional expressions are true.
The problem: write the function that tests that hypothesis for any generated formula. Optimization is not needed. Simpler is better.
I'd use eval instead of defining any new function, as the simplest and the most natural solution. What would others do? If you'd use eval as well, do you think it is important or rare, maybe even 'artificial' example.
Scott McKay
This is a place you could use 'eval', but... - It depends on what the propositions contain. If it's a tiny subset of Lisp (like 'and', 'or', 'not'), a custom evaluator might be nicer. - A custom evaluator might be faster, too, especially if you're doing this in the context of some sort of SAT-solver where you've got lots of propositions, and you might want caching, etc.
In the many years I've been using Lisp, I've used eval so few times, and one of the times was in a debugger where I wanted to evaluate Lisp forms. :-)
Daniel Weinreb:
I'll add that if you can make the tiny subset have no side effects, there are lots of good properties of that.
And if you can make it declarative, that has big advantages, although that might provide not enough power, depending on what you're planning to use this for.
On 23 Sep 2010, at 21:53, Kazimir Majorinc wrote:
OK, but both of you paid more attention to the additional niceties or advantages, unspecified in the problem, than to confirm or reject the claim that eval is the best solution for problem under specified criteria "simpler is better."
This sounds like a very inappropriate reply to me. You've got responses from two of the most experienced Lispers around, not just experienced in Common Lisp, but in a couple of the most important Lisp dialects.
"Simpler is better" is not a criterion, but a highly subjective, untestable and vague idea at best. The fact is that the problems with eval are well known and well described, both in literature and in practice. It's not necessary to discuss them in this forum, which is about professional uses of Common Lisp. I don't consider this a professional topic, because aside from toy examples, using eval most certainly doesn't simplify things, but makes things more complicated in the long run.
On 24.9.2010 14:51, Raymond Wiker wrote:
Instead of using list-construction operations to construct a "function" to be eval'd, you can use the function-construction operator (lambda, that is). In addition to being more conventional (simply by not using eval), it also gives you a mechanism for passing parameters into your functions without resorting to specials. Two mechanisms, in fact - you can use ordinary parameters and closed-over variables.
But it is not simpler than 'eval'. One would use, for example, #'(lambda ()T) instead of 'T. Rewriting this part
The program generates propositional formulas S1, S2, ... using some experimental algorithm. S1, ..., Sn are needed for generation of Sn+1, as typical in deductive systems.
using functions instead of formulas is harder; the functions can be funcalled, and used as building blocks for larger functions. Same like formulas. So far so good. However, some essential operations on formulas (substitution, unification, rules or inference ...) require analysis of the formula.
Similar analysis of the function could be done only by transforming it back into formula with (caddr (function-lambda-expression Si)), and it is not simpler, even if it work, what is not guaranteed according to Hyperspec.
There is a lot of hand-waving in these paragraphs. Do you have an actual problem that you want to solve, or are you making up these requirements along the way?
Pascal
Look at this:
The program generates propositional formulas S1, S2, ... using some experimental algorithm. S1, ..., Sn are needed for generation of Sn+1, as typical in deductive systems. The formulas contain logical constants and already defined operators. The hypothesis is: all generated propositional expressions are true.
The problem: write the function that tests that hypothesis for any generated formula. Optimization is not needed. Simpler is better.
I'd use eval instead of defining any new function, as the simplest and the most natural solution. What would others do? If you'd use eval as well, do you think it is important or rare, maybe even 'artificial' example.
Scott McKay
This is a place you could use 'eval', but... - It depends on what the propositions contain. If it's a tiny subset of Lisp (like 'and', 'or', 'not'), a custom evaluator might be nicer. - A custom evaluator might be faster, too, especially if you're doing this in the context of some sort of SAT-solver where you've got lots of propositions, and you might want caching, etc.
In the many years I've been using Lisp, I've used eval so few times, and one of the times was in a debugger where I wanted to evaluate Lisp forms. :-)
Daniel Weinreb:
I'll add that if you can make the tiny subset have no side effects, there are lots of good properties of that.
And if you can make it declarative, that has big advantages, although that might provide not enough power, depending on what you're planning to use this for.
pro mailing list pro@common-lisp.net http://common-lisp.net/cgi-bin/mailman/listinfo/pro
On 26.9.2010 2:09, Pascal Costanza wrote:
untestable and vague idea at best. The fact is that the problems with eval are well known and well described, both in literature and in practice. It's not necessary to discuss them in this forum, which is about professional uses of Common Lisp. I don't consider this a professional topic, because aside from toy examples, using eval most certainly doesn't simplify things, but makes things more complicated in the long run.
Pascal and others,
Moderator politely proposed that I do not continue sub-discussion on eval in this thread. If you have some references of the literature you mentioned here, please post me privately, off this list.
As main discussion - on best macros - ended, I'll use this opportunity to thank to all who contributed to this thread. I got a number of valuable answers.
Kazimir
On 26 Sep 2010, at 03:33, Kazimir Majorinc wrote:
On 26.9.2010 2:09, Pascal Costanza wrote:
untestable and vague idea at best. The fact is that the problems with eval are well known and well described, both in literature and in practice. It's not necessary to discuss them in this forum, which is about professional uses of Common Lisp. I don't consider this a professional topic, because aside from toy examples, using eval most certainly doesn't simplify things, but makes things more complicated in the long run.
Pascal and others,
Moderator politely proposed that I do not continue sub-discussion on eval in this thread. If you have some references of the literature you mentioned here, please post me privately, off this list.
Good tutorials on Common Lisp (and probably Scheme) should discuss the issues with eval and why to avoid it. From a scientific perspective, the literature on reflection and macro programming is relevant, such as the papers that can be found at http://library.readscheme.org/
I'm happy to discuss the topic in a public forum, but not in private. comp.lang.lisp seems appropriate to me.
Best, Pascal
On Sun, Sep 5, 2010 at 5:24 AM, Kazimir Majorinc kazimir@chem.pmf.hr wrote:
Imagine that someone invited you to write the presentation "Five best CL macros ... I seen" or "Five best CL macros ... I wrote." What would you chose and why?
This is one of my favorite example macros. Although in this simple form it's not something I use in real code, I do use a variation on this theme all the time. The best part about it is, there are few languages other than Lisp in which a tree walk can be so neatly encapsulated into an iteration primitive.
(defmacro do-leaves (var tree &body body) "Walks TREE, a list structure; for any descendant that is not a list, binds VAR to it and executes BODY." `(labels ((rec (,var) (when ,var (if (listp ,var) (progn (rec (car ,var)) (rec (cdr ,var))) (progn ,@body))))) (rec ,tree)))
-- Scott
Hi,
I have a program which takes a mathematical expression for a polynomial and converts it to an equivalent polynomial of the form:
(+ (* ... ) (* ... ) (* ... ) )
where the .... contain only numbers and/or symbols. e.g.
(* (+ A B) (- A B) ) gets converted to (+ (* A A) (* -1 B B))
Once in this "canonical" form I process the polynomial to produce a new function that returns the coefficients of the polynomial for a particular symbol. e.g.
(do-polynomial-coefficients-function 'b '(a) '(+ (* A A) (* -1 B B) )
(LAMBDA (A) (LIST -1 ; (* -1 (expt B 2) ) 0 ; (* 0 (expt B 1) ) (+ (* A A))))
The list of coefficients can then be used to find the roots of the polynomial i.e. what are the values of B for which (zerop (* (+ A B) (- A B)) is true.
The reason I wrote the expander is for the following 6th degree polynomial where I thought I would make more mistakes doing it by hand.
(canonical-polynomial-coefficients-function t-value (a b c d f1 f2)
(- (* t-value (expt (+ (expt (+ (* a t-value) b) 2.0) (* (expt f2 2) (expt (+ (* c t-value) d) 2))) 2)) (* (- (* a d) (* b c)) (expt (+ 1 (* f1 f1 t-value t-value)) 2) (+ (* a t-value) b) (+ (* c t-value) d))))
The aspect I like most about "code is data", is the polynomial above is exactly as written in the text book. It is easy for me to check that it is right, it is also easy to document and think of the amount of paper I didn't need to use.
Mark
On 05/09/2010, at 10:24 PM, Kazimir Majorinc wrote:
As first, good luck with this list!
I'm in search for best examples of "code is data" paradigm in Common Lisp. For most CL-ers, it probably means "macros", but eval, backquote, anything that processes the code as data is of interest. As "best" I think on the most surprising, powerful, sophisticated examples, not necessarily of a pedagogical value.
Imagine that someone invited you to write the presentation "Five best CL macros ... I seen" or "Five best CL macros ... I wrote." What would you chose and why?
Kazimir Majorinc
pro mailing list pro@common-lisp.net http://common-lisp.net/cgi-bin/mailman/listinfo/pro
On Sun, Sep 5, 2010 at 5:24 AM, Kazimir Majorinc kazimir@chem.pmf.hr wrote:
Imagine that someone invited you to write the presentation "Five best CL macros ... I seen" or "Five best CL macros ... I wrote." What would you chose and why?
I look forward to seeing other folks' answers. In the meantime, here are some very short versions of two classic macros that I came up with the other day after someone on #lisp pointed out that my old version of ONCE-ONLY was too complicated. I'm not really sure whether I should be proud or ashamed of these but they're sort of fun and only a few lines of code. (It's also possible I've botched something up and these are busted in some way I haven't realized. If so, I'd be glad to hear about it.)
First, a helper function of no particular interest:
(defun gensyms (names) (mapcar (lambda (x) (gensym (string x))) names))
Then a slightly evil macro that abstracts the pattern of mapping over lists and generating backquoted expressions from their contents. In order to make things concise I assume the lists arguments are in fact going to be symbols naming lists so I can reuse those symbols as names of variables in the backquoted expressions:
(defmacro mapticks (form &rest lists) `(mapcar (lambda (,@lists) ,form) ,@lists))
Now the classic WITH-GENSYMS:
(defmacro with-gensyms ((&rest n) &body body) `(let ,(mapticks `(,n (gensym ,(string n))) n) ,@body))
And ONCE-ONLY:
(defmacro once-only ((&rest n) &body body &aux (g (gensyms n))) ``(let (,,@(mapticks ``(,',g ,,n) g n)) ,(let (,@(mapticks `(,n ',g) n g)) ,@body)))
-Peter
On Sun, Sep 5, 2010 at 5:24 AM, Kazimir Majorinc kazimir@chem.pmf.hr wrote:
As first, good luck with this list!
I'm in search for best examples of "code is data" paradigm in Common Lisp. For most CL-ers, it probably means "macros", but eval, backquote, anything that processes the code as data is of interest. As "best" I think on the most surprising, powerful, sophisticated examples, not necessarily of a pedagogical value.
Imagine that someone invited you to write the presentation "Five best CL macros ... I seen" or "Five best CL macros ... I wrote." What would you chose and why?
Look forward to seeing more great macros from others on the list.
Here's a macro I wrote called `bind' that I got a lot of utility from in a recent project. It was inspired by the `metabang-bind' facility (http://common-lisp.net/project/metabang-bind/), but with simpler and slightly different goals.
From the docstring:
This macro combines the behaviour of the forms `let*', `destructuring-bind', and `multiple-value-bind', permitting the following style of binding form:
(bind (((:values m n) (values 10 20)) ((a b _c &key (d 10)) '(1 2 3)) (x 5)) (+ x a b d m n)) => 48
While Lisp purists may dislike it, I picked up the _foo notation for ignorable vars from Erlang, which can be handy at times.
----------------
(defmacro bind (clauses &body body) "This macro combines the behaviour of the forms `let*', `destructuring-bind', and `multiple-value-bind', permitting the following style of binding form:
(bind (((:values m n) (values 10 20)) ((a b _c &key (d 10)) '(1 2 3)) (x 5)) (+ x a b d m n)) => 48
Note in the destructuring form (a b _c &key (d 10)), _c is a short form for declaring it as ignorable.
This is a more limited and lightweight implementation of some ideas from metabang-bind (http://common-lisp.net/project/metabang-bind/)." (labels ((parse-arglist (args) (loop for arg in args collect arg into args when (and (symbolp arg) (eq (aref (symbol-name arg) 0) #_)) collect arg into ignorables finally (return (values args ignorables)))) (cons-form (form args clauses body) (multiple-value-bind (arglist ignorables) (parse-arglist args) `(,form ,arglist ,@(cdar clauses) ,@(when ignorables `((declare ,(list* 'ignore ignorables)))) (bind ,(cdr clauses) ,@body))))) (cond ((null clauses) `(progn ,@body)) ((listp (caar clauses)) (cond ((eq (caaar clauses) :values) (cons-form 'multiple-value-bind (cdaar clauses) clauses body)) ((eq (caaar clauses) :slots) `(with-slots ,(cdaar clauses) ,@(cdar clauses) (bind ,(cdr clauses) ,@body))) (t (cons-form 'destructuring-bind (caar clauses) clauses body)))) (t `(let (,(car clauses)) (bind ,(cdr clauses) ,@body))))))
----------------
Cheers,
-ram
My favorite macro was suggested by Marco Baringer:
(defmacro with-nesting ((#-ccl &optional) &rest things) (reduce #'(lambda (outer inner) (append outer (list inner))) things :from-end t))
You can then put plenty of binding forms at the same indentation level, without inventing one kind of new syntax per binding form.
I once wrote a program to turn SEXP-syntaxed IBM 370 assembly "datastructure" declarations into Lisp code to decode such. Worked great, but doesn't fit in a page, what with EBCDIC decoding, weird assembly conventions, primitive memory management and other horrors. But the principle was also that inside a PROGX, a definition form (DS PRS H ->date date-string "PARS Date of update") ;@@drop would turn into code that would bind a variable PRS to a value read from current cursor position (post-incremented), interpreted as a date, and add a summary of said value (a string) to an index.
You could then read a reformatted, annotated, tweaked and bugfixed derivative of the original assembly source code as the program to decode same datastructures.
[ François-René ÐVB Rideau | Reflection&Cybernethics | http://fare.tunes.org ] The best place to find a helping hand is at the end of your own arm.
1. Doug Hoyte's book Let Over Lambda, chapter 6.3 Alet and Finite State Machines, raised my macrology to a new level. I used his ideas to implement a full-blown hierarchical state machine macro(s) (roughly 200 lines of CL, too large to post here). The hierarchical state machine paradigm is great for programming reactive things, e.g. gui's, hardware controllers, etc.
2. On Lisp's and PAIP's prolog compiler macros. The code itself is a nice brain stretcher. I use cl prolog to parse difficult languages (e.g. diagram-based languages).
pt
On Mon, Sep 6, 2010 at 10:41 AM, Paul Tarvydas tarvydas@allstream.net wrote:
- Doug Hoyte's book Let Over Lambda, chapter 6.3 Alet and Finite State Machines, raised my macrology to a new level. I used his ideas to implement a full-blown hierarchical state machine macro(s) (roughly 200 lines of CL, too large to post here). The hierarchical state machine paradigm is great for programming reactive things, e.g. gui's, hardware controllers, etc.
If you're willing to share this I'd love to see it. Maybe put it on paste.lisp.org? Or maybe it even deserves to be a common-lisp.net project?
-- Scott
On Mon, 6 Sep 2010 12:26:41 -0700, "Scott L. Burson" Scott@sympoiesis.com wrote:
On Mon, Sep 6, 2010 at 10:41 AM, Paul Tarvydas tarvydas@allstream.net wrote:
- Doug Hoyte's book Let Over Lambda, chapter 6.3 Alet and Finite State Machines, raised my macrology to a new level. I used his ideas to implement a full-blown hierarchical state machine macro(s) (roughly 200 lines of CL, too large to post here). The hierarchical state machine paradigm is great for programming reactive things, e.g. gui's, hardware controllers, etc.
If you're willing to share this I'd love to see it. Maybe put it on paste.lisp.org? Or maybe it even deserves to be a common-lisp.net project?
I'd love to see it likewise!
me too
On Tue, Sep 7, 2010 at 10:14 AM, Samium Gromoff <_deepfire@feelingofgreen.ru
wrote:
On Mon, 6 Sep 2010 12:26:41 -0700, "Scott L. Burson" Scott@sympoiesis.com wrote:
On Mon, Sep 6, 2010 at 10:41 AM, Paul Tarvydas tarvydas@allstream.net
wrote:
- Doug Hoyte's book Let Over Lambda, chapter 6.3 Alet and Finite State
Machines, raised my macrology to a new level. I used his ideas to implement a full-blown hierarchical state machine macro(s) (roughly 200 lines of CL, too large to post here). The hierarchical state machine paradigm is great for programming reactive things, e.g. gui's, hardware controllers, etc.
If you're willing to share this I'd love to see it. Maybe put it on paste.lisp.org? Or maybe it even deserves to be a common-lisp.net project?
I'd love to see it likewise!
-- regards, Samium Gromoff -- "Actually I made up the term 'object-oriented', and I can tell you I did not have C++ in mind." - Alan Kay (OOPSLA 1997 Keynote)
pro mailing list pro@common-lisp.net http://common-lisp.net/cgi-bin/mailman/listinfo/pro
Paul Tarvydas wrote:
- Doug Hoyte's book Let Over Lambda, chapter 6.3 Alet and Finite State Machines, raised my macrology to a new level. I used his ideas to implement a full-blown hierarchical state machine macro(s) (roughly 200 lines of CL, too large to post here). The hierarchical state machine paradigm is great for programming reactive things, e.g. gui's, hardware controllers, etc.
This is an interesting and original book. The techniques he likes, including "anamorphic macros", have generally been frowned upon by the community. However, I don't want to object to his approach just because it's different from that if me and my friends. Who know, he might be on to something. I think it's extremely impressive that he was able to get a book published on a topic with such a small audience: good for him!
-- Dan
- On Lisp's and PAIP's prolog compiler macros. The code itself is a nice brain stretcher. I use cl prolog to parse difficult languages (e.g. diagram-based languages).
pt
pro mailing list pro@common-lisp.net http://common-lisp.net/cgi-bin/mailman/listinfo/pro
On Sep 7, 2010, at 6:19 PM, Daniel Weinreb wrote:
I think it's extremely impressive that he was able to get a book published on a topic with such a small audience: good for him!
It was published through Lulu, which is a print-on-demand service, rather than through a traditional publisher, so what kind of audience it could expect didn't matter. The Lua books use the same service. It seems to work well for niche books like those, though I think Let Over Lambda would have benefitted from a traditional publisher, where an editor would have toned down the bombastic writing a little. It's an interesting enough book to be well worth reading anyway though.
On Tue, Sep 7, 2010 at 6:19 PM, Daniel Weinreb dlw@itasoftware.com wrote: [re: Let Over Lamba]
This is an interesting and original book. The techniques he likes, including "anamorphic macros", have generally been frowned upon by the community.
I don't understand why anaphoric macros are so frowned upon. Like any powerful tool, it's easy to construct pathological situations where they make code more difficult to understand. However, I feel that when tastefully used, they enhance the expressiveness of the language, readibility of code, and they can help to reduce the verbosity of CL of which many outsiders complain. Of course, the prior two sentences apply just as well to macros in general as they do to anaphora.
(I remember an interesting paper by Todd Proebsting advocating anaphoric features for languages, which is evidence that it's not just those out-of-control Lispers who lust after these features: we just get what others dream.)
Cheers,
On Wed, Sep 8, 2010 at 09:59, Julian Squires julian@cipht.net wrote:
I don't understand why anaphoric macros are so frowned upon. Like any powerful tool, it's easy to construct pathological situations where they make code more difficult to understand. However, I feel that when tastefully used, they enhance the expressiveness of the language, readibility of code, and they can help to reduce the verbosity of CL of which many outsiders complain. Of course, the prior two sentences apply just as well to macros in general as they do to anaphora.
The power of macros gives Lisp programmers the freedom to create the language that they need, but every single modification of the language makes it derive from what another programmer would expect. Thus, in a setting where the written code serves not only as instructions to a computer, but also as prose communicating intent to another programmer, it is vital to establish borders to the amount of language modification permitted.
There are certain idiomatic macro styles that are commonly understood amongs Lisp programmers, but the number of these styles is low. One of them is the WITH-family used for implicit (and guaranteed) resource release, another is the DO-family for iteration. These idiomatic styles are easy to recognize and well-established, so they are rather common.
Another family of macros that is everywhere, idiomatic and well-established is top-level definers. That family certainly is much broader, and often a simple top-level definition form expands into loads of code. They are often part of a larger framework, and as top-level forms they are easy to recognize.
Anaphoric macros usually are much harder to recognize visually, and they do "unexpected" things to source code on a micro-level. This is what I object to. Within the body of the anaphoric construct, a simple variable "it" suddenly becomes dependent on the local context. I find code with anaphoric macros harder to read than code that uses explicit variable names, because, in a linear reading flow, I am not told what a certain symbol is bound to. Rather, I need to imagine the "it" myself when reading "awhen". [*]
What I do support is the use of combined test-and-bind macros (WHEN-BIND, WHEN-LET or similar). They serve the same purpose as anaphoric macros (reduce repetetive words in local contexts), but they are much more scalable and require giving things names.
Anaphoric macros are a good example of things that one can do with Common Lisp and that one can also like about Common Lisp, but that are not useful in industrial software development practice. When working together, making things as easy to read as possible is not only a question of style, but also of politeness and responsibility. I am certainly not claiming that the macro types listed above are the only macros that are useful and should be allowed. It is just that anaphoric macros, despite all their cuteness, tax code readers unnecessarily hard and thus have not been adopted as acceptable practice in multi-programmer environments.
-Hans
[*] I never really dug Forth for anything but small programs because, like anaphoric macros in CL, Forth requires me to keep more state in my head when reading code.
Regarding the use of "it":
What if your editor could reveal the referent of "it" with a simple mouse-over? Would that be an adequate solution?
Come to think of it, programming could be easier in general if more information could appear in this "tool-tip" fashion rather than requiring the heavyweight mechanism of applying a tool that opens another window, or worse, looking up documentation in a separate web-browser.
I may be the least professional Lisper in this discussion, and I haven't learned my tools yet. So take my ideas for whatever they're worth.
LW
On Sep 8, 2010, at 8:34 AM, Hans Hübner wrote:
On Wed, Sep 8, 2010 at 09:59, Julian Squires julian@cipht.net wrote:
I don't understand why anaphoric macros are so frowned upon. Like any powerful tool, it's easy to construct pathological situations where they make code more difficult to understand. However, I feel that when tastefully used, they enhance the expressiveness of the language, readibility of code, and they can help to reduce the verbosity of CL of which many outsiders complain. Of course, the prior two sentences apply just as well to macros in general as they do to anaphora.
The power of macros gives Lisp programmers the freedom to create the language that they need, but every single modification of the language makes it derive from what another programmer would expect. Thus, in a setting where the written code serves not only as instructions to a computer, but also as prose communicating intent to another programmer, it is vital to establish borders to the amount of language modification permitted.
There are certain idiomatic macro styles that are commonly understood amongs Lisp programmers, but the number of these styles is low. One of them is the WITH-family used for implicit (and guaranteed) resource release, another is the DO-family for iteration. These idiomatic styles are easy to recognize and well-established, so they are rather common.
Another family of macros that is everywhere, idiomatic and well-established is top-level definers. That family certainly is much broader, and often a simple top-level definition form expands into loads of code. They are often part of a larger framework, and as top-level forms they are easy to recognize.
Anaphoric macros usually are much harder to recognize visually, and they do "unexpected" things to source code on a micro-level. This is what I object to. Within the body of the anaphoric construct, a simple variable "it" suddenly becomes dependent on the local context. I find code with anaphoric macros harder to read than code that uses explicit variable names, because, in a linear reading flow, I am not told what a certain symbol is bound to. Rather, I need to imagine the "it" myself when reading "awhen". [*]
What I do support is the use of combined test-and-bind macros (WHEN-BIND, WHEN-LET or similar). They serve the same purpose as anaphoric macros (reduce repetetive words in local contexts), but they are much more scalable and require giving things names.
Anaphoric macros are a good example of things that one can do with Common Lisp and that one can also like about Common Lisp, but that are not useful in industrial software development practice. When working together, making things as easy to read as possible is not only a question of style, but also of politeness and responsibility. I am certainly not claiming that the macro types listed above are the only macros that are useful and should be allowed. It is just that anaphoric macros, despite all their cuteness, tax code readers unnecessarily hard and thus have not been adopted as acceptable practice in multi-programmer environments.
-Hans
[*] I never really dug Forth for anything but small programs because, like anaphoric macros in CL, Forth requires me to keep more state in my head when reading code.
pro mailing list pro@common-lisp.net http://common-lisp.net/cgi-bin/mailman/listinfo/pro
LW wrote:
What if your editor could reveal the referent of "it" with a simple mouse-over? Would that be an adequate solution?
Come to think of it, programming could be easier in general if more information could appear in this "tool-tip" fashion rather than requiring the heavyweight mechanism of applying a tool that opens another window, or worse, looking up documentation in a separate web-browser.
IIRC, the Dr. Scheme editor had such features back in 1999. They can be very useful on occasion; but the need to interact with them can be a hindrance. Also annoying is when the act of refactoring code breaks the tools used to simplify the process.
At ILC09, Duane Rettig outlined a smart debugger that could step all the way from source through macros down to compiled code. A similar framework may be needed to expose the IT in an anaphoric macro.
- Daniel
dherring@tentpost.com wrote:
LW wrote
IIRC, the Dr. Scheme editor had such features back in 1999.
I have long felt that it's a shame that the Common Lisp community and the Scheme community are so badly splt. I strongly suspect that Common Lisp has a lot to learn from DrScheme.
They can be very useful on occasion; but the need to interact with them can be a hindrance. Also annoying is when the act of refactoring code breaks the tools used to simplify the process.
At ILC09, Duane Rettig outlined a smart debugger that could step all the way from source through macros down to compiled code. A similar framework may be needed to expose the IT in an anaphoric macro.
Very interesting. It sure would be nice if all that good work being done at Franz were open source. (I do understand why it's not.)
--- Dan
- Daniel
pro mailing list pro@common-lisp.net http://common-lisp.net/cgi-bin/mailman/listinfo/pro
That's just Emacs. Most modern tools in the Java or .NET world already do that type of stuff. Apple's Dylan IDE probably would have that stuff 10-12 years ago, but NeXT came around and the rest is history.
On Wed, Sep 8, 2010 at 10:39 PM, Laughing Water lw@mt.net wrote:
Regarding the use of "it":
What if your editor could reveal the referent of "it" with a simple mouse-over? Would that be an adequate solution?
Come to think of it, programming could be easier in general if more information could appear in this "tool-tip" fashion rather than requiring the heavyweight mechanism of applying a tool that opens another window, or worse, looking up documentation in a separate web-browser.
I may be the least professional Lisper in this discussion, and I haven't learned my tools yet. So take my ideas for whatever they're worth.
LW
On Sep 8, 2010, at 8:34 AM, Hans Hübner wrote:
On Wed, Sep 8, 2010 at 09:59, Julian Squires julian@cipht.net wrote:
I don't understand why anaphoric macros are so frowned upon. Like any powerful tool, it's easy to construct pathological situations where they make code more difficult to understand. However, I feel that when tastefully used, they enhance the expressiveness of the language, readibility of code, and they can help to reduce the verbosity of CL of which many outsiders complain. Of course, the prior two sentences apply just as well to macros in general as they do to anaphora.
The power of macros gives Lisp programmers the freedom to create the language that they need, but every single modification of the language makes it derive from what another programmer would expect. Thus, in a setting where the written code serves not only as instructions to a computer, but also as prose communicating intent to another programmer, it is vital to establish borders to the amount of language modification permitted.
There are certain idiomatic macro styles that are commonly understood amongs Lisp programmers, but the number of these styles is low. One of them is the WITH-family used for implicit (and guaranteed) resource release, another is the DO-family for iteration. These idiomatic styles are easy to recognize and well-established, so they are rather common.
Another family of macros that is everywhere, idiomatic and well-established is top-level definers. That family certainly is much broader, and often a simple top-level definition form expands into loads of code. They are often part of a larger framework, and as top-level forms they are easy to recognize.
Anaphoric macros usually are much harder to recognize visually, and they do "unexpected" things to source code on a micro-level. This is what I object to. Within the body of the anaphoric construct, a simple variable "it" suddenly becomes dependent on the local context. I find code with anaphoric macros harder to read than code that uses explicit variable names, because, in a linear reading flow, I am not told what a certain symbol is bound to. Rather, I need to imagine the "it" myself when reading "awhen". [*]
What I do support is the use of combined test-and-bind macros (WHEN-BIND, WHEN-LET or similar). They serve the same purpose as anaphoric macros (reduce repetetive words in local contexts), but they are much more scalable and require giving things names.
Anaphoric macros are a good example of things that one can do with Common Lisp and that one can also like about Common Lisp, but that are not useful in industrial software development practice. When working together, making things as easy to read as possible is not only a question of style, but also of politeness and responsibility. I am certainly not claiming that the macro types listed above are the only macros that are useful and should be allowed. It is just that anaphoric macros, despite all their cuteness, tax code readers unnecessarily hard and thus have not been adopted as acceptable practice in multi-programmer environments.
-Hans
[*] I never really dug Forth for anything but small programs because, like anaphoric macros in CL, Forth requires me to keep more state in my head when reading code.
pro mailing list pro@common-lisp.net http://common-lisp.net/cgi-bin/mailman/listinfo/pro
pro mailing list pro@common-lisp.net http://common-lisp.net/cgi-bin/mailman/listinfo/pro
On Thu, Sep 9, 2010 at 10:26 AM, Mark Haniford markhaniford@gmail.com wrote:
Apple's Dylan IDE probably would have that stuff 10-12 years ago, but NeXT came around and the rest is history.
The demise of Dylan had nothing to do with NeXT. While it's true that the Apple Cambridge office received its coup de grâce when the NeXT acquisition occurred (I was there and laid off because of it) Dylan was unfortunately gone by then.
-tree
Thanks for the clarification Tom. I guess I inferred wrong from the timing of things. I guess my bitterness of Objective-C being the lingua franca of Apple operating systems app development instead of Dylan still shows.
On Thu, Sep 9, 2010 at 9:55 AM, Tom Emerson tremerson@gmail.com wrote:
On Thu, Sep 9, 2010 at 10:26 AM, Mark Haniford markhaniford@gmail.com wrote:
Apple's Dylan IDE probably would have that stuff 10-12 years ago, but NeXT came around and the rest is history.
The demise of Dylan had nothing to do with NeXT. While it's true that the Apple Cambridge office received its coup de grâce when the NeXT acquisition occurred (I was there and laid off because of it) Dylan was unfortunately gone by then.
-tree
-- Tom Emerson tremerson@gmail.com http://treerex.blogspot.com/
pro mailing list pro@common-lisp.net http://common-lisp.net/cgi-bin/mailman/listinfo/pro
Mark,
Mark Haniford wrote:
That's just Emacs. Most modern tools in the Java or .NET world already do that type of stuff. Apple's Dylan IDE probably would have that stuff 10-12 years ago, but NeXT came around and the rest is history.
Unfortunately, there are serious problems introduced by the use of macros. There are "lines of code" that cannot be seen in the source, because they are produced by macros.
One place where this obviously causes problems is code coverage tools. You run all of your tests, and the tool tells you what lines of code didn't ever get tested. (That's an oversimplified nutshell description.) The Clozure team is working on a solutinon to this problem. I don't know to what degree that work could be applied to other areas of IDE capabilites, such as you said.
One of the very first things you want when you start writing macros is for auto-indentation to do the right thing. The IDE can't just know this for a macro.
In case you want to know how we do this, read the rest of this mail. Fasten your seatbelts; we're in for a bumpy ride!
To deal with this, in our system, we collect information for each macro about how it should be indented. For conmmon idiomatic cases, this is done automatically, but you can do it manually or complicated macros or where the automatic generation isn't right. There is a "little language" for declaring how a form should be indented. In some places in the code, the forms of this "little language" are called "indentaiton descriptors" (which I think is a good name).
We load all of the code of our system into Lisp, just as anyone would. During the load, the loaders sees top level forms that associate the names of the macros with a little data structure in the "little language". It puts these into a simple hash table, keyed by the name of the function.
We also load swank (the Lisp side of Slime). This will automatically read in the per-site swank modifications, in site-init.lisp. It sends this back to Slime, with the keyword :indentation-update. (It would be nice if this keyword contained "indentation-descriptor" in its name, but that's not important here.)
This causes Slime to call slime-handle-indentation-update. This puts the new indentation desriptor on both the slime-indent and the common-lisp-indent-function properties of the symbol whose indentation we're been talking about. This gets examined by the code in site-init.el (that's the Emacs side of the per-site stuff). In our case, a GNU Emacs function named "cl-indent:function) gets called. This is a very big and complicated function, which interprets the "little language" (the indentation descriptor).
Next, we have a top-level form in our
libs/slime/site-init.lisp
Next, we have a top-level form in our site-init. What it does is to make a one-element list of the hash table, and then set
swank::*application-hints-tables*
to that list.
Next, when we load swank, we also load some of the nice contributed swank libraries, and one of the ones we load is called
contrib/swank-indentation.lisp
also, back when we loaded slime, we also loaded into GNU Emacs the file
contrib/slime-indentation.el
The Lisp function
symbol-indentation
is re-defined to do indentation by first looking for the symbol in our hash table, and if it's not found, doing what the original symbol-indentation function would have done. (You can see that in swank.lisp.)
If it does find a result in the hash table, that means we stated how the macro should indent, using the "little language". When Slime needs to indent something, one of the things it does is to send a message to Swank that causes Swank to run symbol-indentation
On Wed, Sep 8, 2010 at 10:39 PM, Laughing Water lw@mt.net wrote:
Regarding the use of "it":
What if your editor could reveal the referent of "it" with a simple mouse-over? Would that be an adequate solution?
Come to think of it, programming could be easier in general if more information could appear in this "tool-tip" fashion rather than requiring the heavyweight mechanism of applying a tool that opens another window, or worse, looking up documentation in a separate web-browser.
I may be the least professional Lisper in this discussion, and I haven't learned my tools yet. So take my ideas for whatever they're worth.
LW
On Sep 8, 2010, at 8:34 AM, Hans Hübner wrote:
On Wed, Sep 8, 2010 at 09:59, Julian Squires julian@cipht.net wrote:
I don't understand why anaphoric macros are so frowned upon. Like any powerful tool, it's easy to construct pathological situations where they make code more difficult to understand. However, I feel that when tastefully used, they enhance the expressiveness of the language, readibility of code, and they can help to reduce the verbosity of CL of which many outsiders complain. Of course, the prior two sentences apply just as well to macros in general as they do to anaphora.
The power of macros gives Lisp programmers the freedom to create the language that they need, but every single modification of the language makes it derive from what another programmer would expect. Thus, in a setting where the written code serves not only as instructions to a computer, but also as prose communicating intent to another programmer, it is vital to establish borders to the amount of language modification permitted.
There are certain idiomatic macro styles that are commonly understood amongs Lisp programmers, but the number of these styles is low. One of them is the WITH-family used for implicit (and guaranteed) resource release, another is the DO-family for iteration. These idiomatic styles are easy to recognize and well-established, so they are rather common.
Another family of macros that is everywhere, idiomatic and well-established is top-level definers. That family certainly is much broader, and often a simple top-level definition form expands into loads of code. They are often part of a larger framework, and as top-level forms they are easy to recognize.
Anaphoric macros usually are much harder to recognize visually, and they do "unexpected" things to source code on a micro-level. This is what I object to. Within the body of the anaphoric construct, a simple variable "it" suddenly becomes dependent on the local context. I find code with anaphoric macros harder to read than code that uses explicit variable names, because, in a linear reading flow, I am not told what a certain symbol is bound to. Rather, I need to imagine the "it" myself when reading "awhen". [*]
What I do support is the use of combined test-and-bind macros (WHEN-BIND, WHEN-LET or similar). They serve the same purpose as anaphoric macros (reduce repetetive words in local contexts), but they are much more scalable and require giving things names.
Anaphoric macros are a good example of things that one can do with Common Lisp and that one can also like about Common Lisp, but that are not useful in industrial software development practice. When working together, making things as easy to read as possible is not only a question of style, but also of politeness and responsibility. I am certainly not claiming that the macro types listed above are the only macros that are useful and should be allowed. It is just that anaphoric macros, despite all their cuteness, tax code readers unnecessarily hard and thus have not been adopted as acceptable practice in multi-programmer environments.
-Hans
[*] I never really dug Forth for anything but small programs because, like anaphoric macros in CL, Forth requires me to keep more state in my head when reading code.
pro mailing list pro@common-lisp.net http://common-lisp.net/cgi-bin/mailman/listinfo/pro
pro mailing list pro@common-lisp.net http://common-lisp.net/cgi-bin/mailman/listinfo/pro
pro mailing list pro@common-lisp.net http://common-lisp.net/cgi-bin/mailman/listinfo/pro
[Sorry for the dup, Dan -- my previous attempt didn't go to the list -- didn't notice that they removed the Reply-To: header from the list messages.]
On Thu, Sep 9, 2010 at 8:26 AM, Daniel Weinreb dlw@itasoftware.com wrote:
One of the very first things you want when you start writing macros is for auto-indentation to do the right thing. The IDE can't just know this for a macro.
Huh. I always thought Zmacs did an adequate job of this. Just know what &BODY means and give me COM-INDENT-DIFFERENTLY for special cases, and I'm pretty happy.
(Dan will of course know what I'm referring to, but for everyone else, the Zmacs command COM-INDENT-DIFFERENTLY -- bound to C-Tab, if memory serves -- could be used to manually cycle through a series of alternate indentation points for the current line. I'm not aware of an equivalent command having been implemented for GNU Emacs.)
-- Scott
Hans,
Wow! That is beautifully explained! I would go so far as to say that even Peter Siebel's work could be enhanced by this explanation. This is the best description, with reasoning, of when to use macros and when not, that I have ever heard (read).
-- Dan
Hans Hübner wrote:
On Wed, Sep 8, 2010 at 09:59, Julian Squires julian@cipht.net wrote:
I don't understand why anaphoric macros are so frowned upon. Like any powerful tool, it's easy to construct pathological situations where they make code more difficult to understand. However, I feel that when tastefully used, they enhance the expressiveness of the language, readibility of code, and they can help to reduce the verbosity of CL of which many outsiders complain. Of course, the prior two sentences apply just as well to macros in general as they do to anaphora.
The power of macros gives Lisp programmers the freedom to create the language that they need, but every single modification of the language makes it derive from what another programmer would expect. Thus, in a setting where the written code serves not only as instructions to a computer, but also as prose communicating intent to another programmer, it is vital to establish borders to the amount of language modification permitted.
There are certain idiomatic macro styles that are commonly understood amongs Lisp programmers, but the number of these styles is low. One of them is the WITH-family used for implicit (and guaranteed) resource release, another is the DO-family for iteration. These idiomatic styles are easy to recognize and well-established, so they are rather common.
Another family of macros that is everywhere, idiomatic and well-established is top-level definers. That family certainly is much broader, and often a simple top-level definition form expands into loads of code. They are often part of a larger framework, and as top-level forms they are easy to recognize.
Anaphoric macros usually are much harder to recognize visually, and they do "unexpected" things to source code on a micro-level. This is what I object to. Within the body of the anaphoric construct, a simple variable "it" suddenly becomes dependent on the local context. I find code with anaphoric macros harder to read than code that uses explicit variable names, because, in a linear reading flow, I am not told what a certain symbol is bound to. Rather, I need to imagine the "it" myself when reading "awhen". [*]
What I do support is the use of combined test-and-bind macros (WHEN-BIND, WHEN-LET or similar). They serve the same purpose as anaphoric macros (reduce repetetive words in local contexts), but they are much more scalable and require giving things names.
Anaphoric macros are a good example of things that one can do with Common Lisp and that one can also like about Common Lisp, but that are not useful in industrial software development practice. When working together, making things as easy to read as possible is not only a question of style, but also of politeness and responsibility. I am certainly not claiming that the macro types listed above are the only macros that are useful and should be allowed. It is just that anaphoric macros, despite all their cuteness, tax code readers unnecessarily hard and thus have not been adopted as acceptable practice in multi-programmer environments.
-Hans
[*] I never really dug Forth for anything but small programs because, like anaphoric macros in CL, Forth requires me to keep more state in my head when reading code.
pro mailing list pro@common-lisp.net http://common-lisp.net/cgi-bin/mailman/listinfo/pro
On 9 sep 2010, at 16:45, Daniel Weinreb dlw@itasoftware.com wrote:
This is the best description, with reasoning, of when to use macros and when not, that I have ever heard (read).
Agreed, it summed up my feelings on AIF, AWHEN, etc. while also introducing me to better alternatives so I don't have to write them myself.
On Wed, Sep 8, 2010 at 10:34 AM, Hans Hübner hans.huebner@gmail.com wrote:
The power of macros gives Lisp programmers the freedom to create the language that they need, but every single modification of the language makes it derive from what another programmer would expect. Thus, in a setting where the written code serves not only as instructions to a computer, but also as prose communicating intent to another programmer, it is vital to establish borders to the amount of language modification permitted.
I want to say that I largely agree with what you're saying, but disagree with some specifics as a matter of opinion. I'm not sure further discussion of it is warranted here, but I finally felt compelled to mention two things which help reveal where our opinions split:
Forth (which I love) relates to other terse means of expression (anaphoric macros, DSLs, J) in that they reward factoring large problems into tiny pieces (in some cases, perversely, by punishing other approaches). This reduction and crystallization process is one way of dealing with so many of the problems associated with "industrial software development". (Also, if the thought "to what does /it/ refer?" even momentarily crosses my mind, it's time to factor that bit into its own function.)
The other aspect is that software development in any language requires a certain degree of good faith on the part of both the author and the reader. The reader has an obligation to interpret the author carefully -- I have never seen a large software project where it was unnecessary to carefully examine headers and macros, the build system, utilities, et cetera, to really understand what was happening. So, while I agree that borders on language modification must be drawn, I suggest that those borders be drawn at a project level rather than a language community level.
I like to use SERIES as well as various pattern matching tools -- these also seem like they might fall outside your list of accepted macro styles, which would be a shame, since they've been around for ages, and to me, can increase the legibility of the code by allowing a more precise (and secondarily, succinct) expression of intent. When I act as a reader, I study the author's choice of notation in good faith that they have chosen it to ultimately improve their communication with me.
Julian,
thank you for adding your opinion to the discussion. I agree with what you write, and I certainly did not mean to come up with a universal definition as to what macro styles would be acceptable. My intent was to come up with some more reasoning as to why anaphoric macros have not been picked up widely, even though they are compellingly cute and a strikingly good example of the power of Common Lisp macros. They are an example.
Industrial software development as I meant it refers to projects that consist of many people and that have a relatively large fluctuation in the developer base. In such contexts, the number of unusual constructs should be kept low, and it is not generally a good idea to allow individual programmers to modify the language as such. If a project as a whole decides to pick up particular macros or language extensions, it is helpful to choose extensions which add value beyond cuteness. Also, it is good to allow only language extensions that are either idiomatic (i.e. it is easy to infer the meaning of private constructs because a certain extension pattern is followed, like WITH-* or DO-*), or that are documented well. Being disciplined about language extensions helps getting new people into the project, and also benefits the overall quality of the source base.
SERIES is a particularily good example of an extension that is large and documented: It does something that can be valuable and that can't easily be emulated with standard idioms. It is also documented in a way that makes it possible, for someone joining in to a project, to really learn what it is capable of doing. If I'd be the language lawyer in a new project, SERIES might be a candidate for inclusion. In an existing project, I'm not sure if I would want to be the one that started using it. Embedded Prolog, CLOS and AspectL are other examples of such a heavyweight language extensions that one might want to choose, consciously. One may also want to disallow the use of certain language extensions even though they're in the ANSI standard (heh).
This list is meant to be a forum for professional programmers and I am well aware of the fact that many of you don't require any advice on this level. I'm just trying to banter, mind you.
-Hans
On Thu, Sep 9, 2010 at 19:23, Julian Squires julian@cipht.net wrote:
On Wed, Sep 8, 2010 at 10:34 AM, Hans Hübner hans.huebner@gmail.com wrote:
The power of macros gives Lisp programmers the freedom to create the language that they need, but every single modification of the language makes it derive from what another programmer would expect. Thus, in a setting where the written code serves not only as instructions to a computer, but also as prose communicating intent to another programmer, it is vital to establish borders to the amount of language modification permitted.
I want to say that I largely agree with what you're saying, but disagree with some specifics as a matter of opinion. I'm not sure further discussion of it is warranted here, but I finally felt compelled to mention two things which help reveal where our opinions split:
Forth (which I love) relates to other terse means of expression (anaphoric macros, DSLs, J) in that they reward factoring large problems into tiny pieces (in some cases, perversely, by punishing other approaches). This reduction and crystallization process is one way of dealing with so many of the problems associated with "industrial software development". (Also, if the thought "to what does /it/ refer?" even momentarily crosses my mind, it's time to factor that bit into its own function.)
The other aspect is that software development in any language requires a certain degree of good faith on the part of both the author and the reader. The reader has an obligation to interpret the author carefully -- I have never seen a large software project where it was unnecessary to carefully examine headers and macros, the build system, utilities, et cetera, to really understand what was happening. So, while I agree that borders on language modification must be drawn, I suggest that those borders be drawn at a project level rather than a language community level.
I like to use SERIES as well as various pattern matching tools -- these also seem like they might fall outside your list of accepted macro styles, which would be a shame, since they've been around for ages, and to me, can increase the legibility of the code by allowing a more precise (and secondarily, succinct) expression of intent. When I act as a reader, I study the author's choice of notation in good faith that they have chosen it to ultimately improve their communication with me.
-- Julian Squires
pro mailing list pro@common-lisp.net http://common-lisp.net/cgi-bin/mailman/listinfo/pro
macros that are useful and should be allowed. It is just that anaphoric macros, despite all their cuteness, tax code readers unnecessarily hard and thus have not been adopted as acceptable practice in multi-programmer environments.
well, it's one opinion.
there's at least one counter example in our team of 4. and i personally do like reasonable usage of anaphoric macros, even when done by my colleagues. and the reason is exactly that it *rises* code readability for us.
our practice includes special coloring for "it" and avoids any usage which is not blatantly trivial.
Attila Lendvai wrote:
macros that are useful and should be allowed. It is just that anaphoric macros, despite all their cuteness, tax code readers unnecessarily hard and thus have not been adopted as acceptable practice in multi-programmer environments.
well, it's one opinion.
there's at least one counter example in our team of 4. and i personally do like reasonable usage of anaphoric macros, even when done by my colleagues. and the reason is exactly that it *rises* code readability for us.
our practice includes special coloring for "it" and avoids any usage which is not blatantly trivial.
OK, if you're going to do that, that helps a lot.
A problem with this is that code that starts blatently trivial can grow. This is one reason that you see enormous Perl scripts; people thought they'd be simple enough that Perl was appropriate, but they grow over time.
Suddenly you have an outer "it" and an inner "it", and you need to "let" the outer one if you need to access that quantity in the inner one. And someone reading the code can get confuse about which one you mean.
Instead of "awhen", what we use here is called when-bind. Example (real one, from actual airline code):
(or (when-bind (dmc (message-dmc (request-task-message rt))) (dmc-struct-maximum-number-of-fdrs-in-response dmc)) (iatci-config :max-response-fdrs rt nil))
You specify the name. It's not anamorphic. The name can be meaningful, rather than "it". There are no conflicts. It does not get ugly when the code gets larger.
The downside is that it's more verbose, because you have to put in the name of the variable, and there is one more level of paren to keep things Lispy.
By the way, there is a sort of anamorphic thing in the "Clojure" dialect, where you can write little lambdas without arglists, by using $1 and $2 and so on to mean first arg, second arg, and so on. Again, he's trying to make programming with functions more friendly by making it more succinct, and I'm sure he'd also say "you should only use it for simple cases".
If you are following good practices, you'd stop using these things when the code gets large. Having automatic refactoring IDE's that could do that would be one way of trying to get the best of both words.
On the other hand, we do have an anmorphic macro that we sometimes use, in the sense that it makes up its own symbols. It is called define-class, and tries to be more like the Flavors class definer. The CLOS one, I think in an attempt to entirely avoid anamorphism, makes you spell out the names of the accessors (and/or readers and writers). define-class creates these names.
(define-class puma-request-context (migration-request-context) ((journey :type qapi-journey) (pnr :type qapi-pnr) (staged-pnr :type staged-puma-pnr) (pax-map :type list) ; a list containing elements of form (staged-pax-name . qapi-pnr-passenger) (seg-map :type list) ; a list containing elements of form (staged-segment . qapi-pnr-segment) (slices :type list) ; a list of qapi-pnr-slice (staging-to-action-map ;; A list of two element sublists associating a staging object with a QAPI action. :type list :initform nil)))
expands into
(PROGN (DEFCLASS PUMA-REQUEST-CONTEXT (MIGRATION-REQUEST-CONTEXT) ((JOURNEY :ACCESSOR PUMA-REQUEST-CONTEXT-JOURNEY :INITARG :JOURNEY :TYPE QAPI-JOURNEY) (PNR :ACCESSOR PUMA-REQUEST-CONTEXT-PNR :INITARG :PNR :TYPE QAPI-PNR) (STAGED-PNR :ACCESSOR PUMA-REQUEST-CONTEXT-STAGED-PNR :INITARG :STAGED-PNR :TYPE STAGED-PUMA-PNR) (PAX-MAP :ACCESSOR PUMA-REQUEST-CONTEXT-PAX-MAP :INITARG :PAX-MAP :TYPE LIST) (SEG-MAP :ACCESSOR PUMA-REQUEST-CONTEXT-SEG-MAP :INITARG :SEG-MAP :TYPE LIST) (SLICES :ACCESSOR PUMA-REQUEST-CONTEXT-SLICES :INITARG :SLICES :TYPE LIST) (STAGING-TO-ACTION-MAP :ACCESSOR PUMA-REQUEST-CONTEXT-STAGING-TO-ACTION-MAP :INITARG :STAGING-TO-ACTION-MAP :TYPE LIST :INITFORM NIL))) (DECLARE-LIST-OF PUMA-REQUEST-CONTEXT) (DEFUN MAKE-PUMA-REQUEST-CONTEXT (&REST QUUX::KEYS &KEY JOURNEY PNR STAGED-PNR PAX-MAP SEG-MAP SLICES STAGING-TO-ACTION-MAP &ALLOW-OTHER-KEYS) (DECLARE (IGNORABLE JOURNEY PNR STAGED-PNR PAX-MAP SEG-MAP SLICES STAGING-TO-ACTION-MAP)) (DECLARE (DYNAMIC-EXTENT QUUX::KEYS)) (APPLY #'MAKE-INSTANCE 'PUMA-REQUEST-CONTEXT QUUX::KEYS)) (DEFINE-COMPILER-MACRO MAKE-PUMA-REQUEST-CONTEXT (&REST QUUX::ARGS) (LIST* 'MAKE-INSTANCE (LIST* (LIST* 'QUOTE (LIST 'PUMA-REQUEST-CONTEXT)) QUUX::ARGS))) (FIND-CLASS 'PUMA-REQUEST-CONTEXT))
Some of our code uses this and some does not, at the whim of the programmer (perhaps that's poor practice). However, I haven't noticed this causing any problems.
I always liked Erik Naggum's name for your WHEN-BIND: WHEREAS
http://groups.google.com/group/comp.lang.lisp/msg/bc7772aa5ab1f3e4
-Peter
On Tue, Sep 28, 2010 at 3:04 PM, Daniel Weinreb dlw@itasoftware.com wrote:
Attila Lendvai wrote:
macros that are useful and should be allowed. It is just that anaphoric macros, despite all their cuteness, tax code readers unnecessarily hard and thus have not been adopted as acceptable practice in multi-programmer environments.
well, it's one opinion.
there's at least one counter example in our team of 4. and i personally do like reasonable usage of anaphoric macros, even when done by my colleagues. and the reason is exactly that it *rises* code readability for us.
our practice includes special coloring for "it" and avoids any usage which is not blatantly trivial.
OK, if you're going to do that, that helps a lot.
A problem with this is that code that starts blatently trivial can grow. This is one reason that you see enormous Perl scripts; people thought they'd be simple enough that Perl was appropriate, but they grow over time.
Suddenly you have an outer "it" and an inner "it", and you need to "let" the outer one if you need to access that quantity in the inner one. And someone reading the code can get confuse about which one you mean.
Instead of "awhen", what we use here is called when-bind. Example (real one, from actual airline code):
(or (when-bind (dmc (message-dmc (request-task-message rt))) (dmc-struct-maximum-number-of-fdrs-in-response dmc)) (iatci-config :max-response-fdrs rt nil))
You specify the name. It's not anamorphic. The name can be meaningful, rather than "it". There are no conflicts. It does not get ugly when the code gets larger.
The downside is that it's more verbose, because you have to put in the name of the variable, and there is one more level of paren to keep things Lispy.
By the way, there is a sort of anamorphic thing in the "Clojure" dialect, where you can write little lambdas without arglists, by using $1 and $2 and so on to mean first arg, second arg, and so on. Again, he's trying to make programming with functions more friendly by making it more succinct, and I'm sure he'd also say "you should only use it for simple cases".
If you are following good practices, you'd stop using these things when the code gets large. Having automatic refactoring IDE's that could do that would be one way of trying to get the best of both words.
On the other hand, we do have an anmorphic macro that we sometimes use, in the sense that it makes up its own symbols. It is called define-class, and tries to be more like the Flavors class definer. The CLOS one, I think in an attempt to entirely avoid anamorphism, makes you spell out the names of the accessors (and/or readers and writers). define-class creates these names.
(define-class puma-request-context (migration-request-context) ((journey :type qapi-journey) (pnr :type qapi-pnr) (staged-pnr :type staged-puma-pnr) (pax-map :type list) ; a list containing elements of form (staged-pax-name . qapi-pnr-passenger) (seg-map :type list) ; a list containing elements of form (staged-segment . qapi-pnr-segment) (slices :type list) ; a list of qapi-pnr-slice (staging-to-action-map ;; A list of two element sublists associating a staging object with a QAPI action. :type list :initform nil)))
expands into
(PROGN (DEFCLASS PUMA-REQUEST-CONTEXT (MIGRATION-REQUEST-CONTEXT) ((JOURNEY :ACCESSOR PUMA-REQUEST-CONTEXT-JOURNEY :INITARG :JOURNEY :TYPE QAPI-JOURNEY) (PNR :ACCESSOR PUMA-REQUEST-CONTEXT-PNR :INITARG :PNR :TYPE QAPI-PNR) (STAGED-PNR :ACCESSOR PUMA-REQUEST-CONTEXT-STAGED-PNR :INITARG :STAGED-PNR :TYPE STAGED-PUMA-PNR) (PAX-MAP :ACCESSOR PUMA-REQUEST-CONTEXT-PAX-MAP :INITARG :PAX-MAP :TYPE LIST) (SEG-MAP :ACCESSOR PUMA-REQUEST-CONTEXT-SEG-MAP :INITARG :SEG-MAP :TYPE LIST) (SLICES :ACCESSOR PUMA-REQUEST-CONTEXT-SLICES :INITARG :SLICES :TYPE LIST) (STAGING-TO-ACTION-MAP :ACCESSOR PUMA-REQUEST-CONTEXT-STAGING-TO-ACTION-MAP :INITARG :STAGING-TO-ACTION-MAP :TYPE LIST :INITFORM NIL))) (DECLARE-LIST-OF PUMA-REQUEST-CONTEXT) (DEFUN MAKE-PUMA-REQUEST-CONTEXT (&REST QUUX::KEYS &KEY JOURNEY PNR STAGED-PNR PAX-MAP SEG-MAP SLICES STAGING-TO-ACTION-MAP &ALLOW-OTHER-KEYS) (DECLARE (IGNORABLE JOURNEY PNR STAGED-PNR PAX-MAP SEG-MAP SLICES STAGING-TO-ACTION-MAP)) (DECLARE (DYNAMIC-EXTENT QUUX::KEYS)) (APPLY #'MAKE-INSTANCE 'PUMA-REQUEST-CONTEXT QUUX::KEYS)) (DEFINE-COMPILER-MACRO MAKE-PUMA-REQUEST-CONTEXT (&REST QUUX::ARGS) (LIST* 'MAKE-INSTANCE (LIST* (LIST* 'QUOTE (LIST 'PUMA-REQUEST-CONTEXT)) QUUX::ARGS))) (FIND-CLASS 'PUMA-REQUEST-CONTEXT))
Some of our code uses this and some does not, at the whim of the programmer (perhaps that's poor practice). However, I haven't noticed this causing any problems.
pro mailing list pro@common-lisp.net http://common-lisp.net/cgi-bin/mailman/listinfo/pro
On Wed, Sep 8, 2010 at 2:59 PM, Julian Squires julian@cipht.net wrote:
On Tue, Sep 7, 2010 at 6:19 PM, Daniel Weinreb dlw@itasoftware.com wrote: [re: Let Over Lamba]
This is an interesting and original book. The techniques he likes, including "anamorphic macros", have generally been frowned upon by the community.
I don't understand why anaphoric macros are so frowned upon.
Everyone keeps talking about anaphoric. Is "anamorphic" a typo, an interchangeable alternative, or something entirely different?
-- Andy
On Wed, Sep 8, 2010 at 4:41 PM, Andy Chambers achambers.home@gmail.com wrote:
On Wed, Sep 8, 2010 at 2:59 PM, Julian Squires julian@cipht.net wrote:
On Tue, Sep 7, 2010 at 6:19 PM, Daniel Weinreb dlw@itasoftware.com wrote: [re: Let Over Lamba]
This is an interesting and original book. The techniques he likes, including "anamorphic macros", have generally been frowned upon by the community.
I don't understand why anaphoric macros are so frowned upon.
Everyone keeps talking about anaphoric. Is "anamorphic" a typo, an interchangeable alternative, or something entirely different?
"Anaphoric" is from linguistics; http://bit.ly/LEZNW (wikipedia).
I have a 64-bit integer obtained by the FLI from external storage. It was obtained by reading two 32-bit unsigned values and depositing them into a zero valued integer. Now I want to sign-extend the result so that if it has its MSB set, the value will be a twos-complement negative value.
(defun read-int64 (ptr) (let ((v (read-uint64 ptr))) (if (logbitp 63 v) (- v #.(ash 1 64)) v)))
But this looks inelegant to me, requiring the storage of a constant #.(ash 1 64) used in a subtraction operation.
Been racking my brain on the BOOLE operations and the LOGNOT et al, looking for a more elegant solution to this. Not a high-value item, just curious.
Dr. David McClain Chief Technical Officer Refined Audiometrics Laboratory 4391 N. Camino Ferreo Tucson, AZ 85750
email: dbm@refined-audiometrics.com phone: 1.520.390.3995 web: http://www.refined-audiometrics.com
On 8 September 2010 13:18, David McClain dbm@refined-audiometrics.com wrote:
I have a 64-bit integer obtained by the FLI from external storage. It was obtained by reading two 32-bit unsigned values and depositing them into a zero valued integer. Now I want to sign-extend the result so that if it has its MSB set, the value will be a twos-complement negative value. (defun read-int64 (ptr) (let ((v (read-uint64 ptr))) (if (logbitp 63 v) (- v #.(ash 1 64)) v))) But this looks inelegant to me, requiring the storage of a constant #.(ash 1 64) used in a subtraction operation. Been racking my brain on the BOOLE operations and the LOGNOT et al, looking for a more elegant solution to this. Not a high-value item, just curious.
(if (logbitp 63 v) (dpb v (byte 64 0) -1) v)
[ François-René ÐVB Rideau | Reflection&Cybernethics | http://fare.tunes.org ] Nostalgia isn’t what it used to be.
I found one way...
(defun read-int64 (ptr) (let ((v (read-uint64 ptr))) (declare (type (unsigned-byte 64) v)) (if (logbitp 63 v) (dpb v (byte 64 0) -1) ;; <-- better? ;; else v))
Dr. David McClain Chief Technical Officer Refined Audiometrics Laboratory 4391 N. Camino Ferreo Tucson, AZ 85750
email: dbm@refined-audiometrics.com phone: 1.520.390.3995 web: http://refined-audiometrics.com
On Sep 8, 2010, at 10:18, David McClain wrote:
I have a 64-bit integer obtained by the FLI from external storage. It was obtained by reading two 32-bit unsigned values and depositing them into a zero valued integer. Now I want to sign- extend the result so that if it has its MSB set, the value will be a twos-complement negative value.
(defun read-int64 (ptr) (let ((v (read-uint64 ptr))) (if (logbitp 63 v) (- v #.(ash 1 64)) v)))
But this looks inelegant to me, requiring the storage of a constant #.(ash 1 64) used in a subtraction operation.
Been racking my brain on the BOOLE operations and the LOGNOT et al, looking for a more elegant solution to this. Not a high-value item, just curious.
Dr. David McClain Chief Technical Officer Refined Audiometrics Laboratory 4391 N. Camino Ferreo Tucson, AZ 85750
email: dbm@refined-audiometrics.com phone: 1.520.390.3995 web: http://www.refined-audiometrics.com
pro mailing list pro@common-lisp.net http://common-lisp.net/cgi-bin/mailman/listinfo/pro
I see.
Well, I'll still go so far as to say that he did write a book's worth of stuff, the writing style is quite nice, and he has unique ideas. So despite the fact that I'm not going to use his techniques, I am nevertheless very impressed.
-- Dan
Julian Squires wrote:
On Tue, Sep 7, 2010 at 6:19 PM, Daniel Weinreb dlw@itasoftware.com wrote: [re: Let Over Lamba]
This is an interesting and original book. The techniques he likes, including "anamorphic macros", have generally been frowned upon by the community.
I don't understand why anaphoric macros are so frowned upon. Like any powerful tool, it's easy to construct pathological situations where they make code more difficult to understand. However, I feel that when tastefully used, they enhance the expressiveness of the language, readibility of code, and they can help to reduce the verbosity of CL of which many outsiders complain. Of course, the prior two sentences apply just as well to macros in general as they do to anaphora.
(I remember an interesting paper by Todd Proebsting advocating anaphoric features for languages, which is evidence that it's not just those out-of-control Lispers who lust after these features: we just get what others dream.)
Cheers,
* 2010-09-05 14:24 (+0200), Kazimir Majorinc wrote:
Imagine that someone invited you to write the presentation "Five best CL macros ... I seen" or "Five best CL macros ... I wrote." What would you chose and why?
Just regular syntactic abstractions here. I wanted to execute a series of regular expression replacements for a string:
(with-regexp-series STRING ("[^a-z0-9._-]" "_") ;forms are ("regexp" "replacement") ("[_-]*\.+[_-]*" ".") ("_*-+_*" "-") ("([._-])\1+" "\1") ("^_+(.)" "\1") ("(.)_+$" "\1"))
The macros:
(defmacro with-value-passing ((var expression) &body body) `(let ((,var ,expression)) ,@(loop for top-level in body collect `(setf ,var ,top-level))))
(defmacro with-regexp-series (string &rest clauses) (let ((value (gensym))) `(with-value-passing (,value ,string) ,@(loop for clause in clauses collect `(ppcre:regex-replace-all ,(first clause) ,value ,(second clause))))))
WITH-VALUE-PASSING is a general-purpose macro which binds a new variable (VAR) and gives it the value of EXPRESSION. Then every top-level form in the macro's body is evaluated and each time the value is assigned to the VAR. It's just a way to run series of commands that depend on previous command's return value. I think it's more readable than deeply nested function calls.
All the elaborate macros aside, here is a very common idiom that takes direct advantage of the "code-is-data" aspects of Lisp:
(fli:copy-pointer p :type `(:c-array ,eltyp ,nel))
That back-quoted parameter construction shows a dynamic construction of a function call, taking runtime data and making it part of code. The exact function being called is unimportant here. I just copied it from some code that I had laying about, extracted from this function:
(defmethod row-major-array-pointer ((p fli::pointer)) (let ((dims (fli:foreign-array-dimensions p))) (if (rank-1-p dims) p (let* ((nel (reduce #'* dims)) (eltyp (fli:foreign-array-element-type p))) (fli:copy-pointer p :type `(:c-array ,eltyp ,nel)))) ;; <-- here is the dynamic construction... ))
Dr. David McClain Chief Technical Officer Refined Audiometrics Laboratory 4391 N. Camino Ferreo Tucson, AZ 85750
email: dbm@refined-audiometrics.com phone: 1.520.390.3995 web: http://refined-audiometrics.com
On Sep 5, 2010, at 05:24, Kazimir Majorinc wrote:
As first, good luck with this list!
I'm in search for best examples of "code is data" paradigm in Common Lisp. For most CL-ers, it probably means "macros", but eval, backquote, anything that processes the code as data is of interest. As "best" I think on the most surprising, powerful, sophisticated examples, not necessarily of a pedagogical value.
Imagine that someone invited you to write the presentation "Five best CL macros ... I seen" or "Five best CL macros ... I wrote." What would you chose and why?
Kazimir Majorinc
pro mailing list pro@common-lisp.net http://common-lisp.net/cgi-bin/mailman/listinfo/pro
I recently wrote a macro that expands forms like this into an airline flight itinerary. It's used in test code, in which we don't want to hard-wire the itineraries because we want to be able to test multiple airlines.
What's interesting about this is that it's actually a constraint language where the variables (like $A, $B, $DATE) get filled in and tested to see if they meet the constraints. This requires Prolog-like backtracking, and the final example uses a lazy- evaluation-like technique so that $S1 can be "bound" and used in the same clause.
(Heh, trying doing this in Java.)
;; Binds $S1, $S2, $S3 to specific flights (with-test-itinerary (($S1 (:AC 8334 "2007-11-13" :BOS :CHI)) ($S2 (:AC 8470 "2007-11-13" :CHI :SFO)) ($S3 (:AC 1156 "2007-11-14" :SFO :BOS))) (values $S1 $S2 $S3))
;; Makes a 3-segment itinerary from A to B to C, ;; with the third segment one day later than the first two (with-test-itinerary (($S1 (:hosted :* $DATE $A $B)) ($S2 (:hosted :* $DATE $B $C) ) ($S3 (:hosted :* (plus-days $DATE 1) $C $A))) (values $S1 $S2 $S3))
;; Makes a 3-segment itinerary from A to B to C, ;; with the third segment one day later than the first two, ;; and the first two segments have the same flight number (with-test-itinerary (($S1 (:hosted $F1 $DATE $A $B)) ($S2 (:hosted $F1 $DATE $B $C)) ($S3 (:hosted $F2 (plus-days $DATE 1) $C $A))) (values $S1 $S2 $S3))
;; Makes a 3-segment itinerary with different flight numbers ;; for each segment (with-test-itinerary (($S1 (:hosted $F1 :* :* :*)) ($S2 (:hosted $F2 :* :* :*)) ($S3 (:hosted $F3 :* :* :*))) (values $S1 $S2 $S3))
;; Makes a 4-segment itinerary from A to B to C to D, ;; the last two segments being two days later, and the ;; final airport satisfies the predicate "far away from C" (with-test-itinerary (($S1 (:hosted :* $DATE :* $A)) ($S2 (:hosted :* $DATE $A $B) ) ($S3 (:hosted :* ($DATE2 (plus-days $DATE 1)) $B $C)) ($S4 (:hosted :* $DATE2 (far-away-airport $C) $D) )) (values $S1 $S2 $S3 $S4))
;; See if you can figure this one out (with-test-itinerary (($S1 (($C1 (and (segment-hosted-carrier-p $S1) (OA-operated-p $S1) $C1)) :* :* :* :*)) ($S2 (($C2 (and (segment-hosted-carrier-p $S2) (not (OA-operated-p $S2)) $C2)) :* :* :* :*))) (values $S1 $S2))
On Sep 5, 2010, at 8:24 AM, Kazimir Majorinc wrote:
As first, good luck with this list!
I'm in search for best examples of "code is data" paradigm in Common Lisp. For most CL-ers, it probably means "macros", but eval, backquote, anything that processes the code as data is of interest. As "best" I think on the most surprising, powerful, sophisticated examples, not necessarily of a pedagogical value.
Imagine that someone invited you to write the presentation "Five best CL macros ... I seen" or "Five best CL macros ... I wrote." What would you chose and why?
Kazimir Majorinc
pro mailing list pro@common-lisp.net http://common-lisp.net/cgi-bin/mailman/listinfo/pro