[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: how expressive are they?



Generally, as folks have pointed out, we can replace anaphoric macros
with functions that take lambdas as arguments.  So 
(aif (foo) (bar it)), where aif is pg's anaphoric if, becomes
(aif-func (foo) (lambda (it) (bar it))).  In a hypothetical Lisp with
Smalltalk-style terser lambdas, that might become 
(aif (foo) [:it | bar it]), which isn't much worse than the original
macro version.

That doesn't avoid the need for macros in some cases, though.  For
example, database languages often have facilities to execute your code
in an environment containing the fields of some database record, once
for each record.  Sometimes I wish I had this facility in
general-purpose languages.  Listing all the fields defined at the
beginning of your block would bloat the code far too much --- most
blocks would contain far more argument list than executable code ---
and would lead to argument-passing bugs and loads of unnecessary
maintenance, as every field addition or deletion would require massive
code rework.

Other languages support other macro-free approaches, though.  In
Python, I can do this:

for r in query.results():
    if r.foo + r.bar > 3: print r.baz + r.quux

Even though Python doesn't introduce r's fields into my evaluation
namespace directly, it leaves them close enough that the above code
doesn't hurt my eyes.  

Compare to Perl's syntax:

for ($query->results) {
  print $_->{baz} + $_->{quux} if $_->{foo} + $_->{bar} > 3;
}

The Python code is nearly as clean as SQL:

select baz + quux from query where foo + bar > 3

(I know these statements have important semantic differences, but I
hope they serve as plausible illustrations of the problem I mean.)

> 3. In your HTML from Perl example, if all this was done at "run-time"
> how did you debug production systems that you did not have access to?
> (The compile-time vs. run-time distinction for data-driven programs
> is not a trivial one). 

Can you explain this question further?  I can't imagine how I would
debug any system I didn't have access to, regardless of how it worked.

> Kragen asks:
> > Do macros offer *anything* other than shorter programs?
> 
> Gee.. I'd turn that around and ask of languages that don't have macros
> .. "what do you offer me in terms of facilities to write shorter
> programs?" It's not exactly a small advantage :)

Yes, I find that advantage important, too.

Do macros offer any other advantages, like better separation of
concerns, reduction of duplication, etc.?  I have seen several
assertions that they do reduce duplication in ways that lightweight
lambda syntax can't, or that they do it more easily, but I haven't
seen any convincing examples.

(Here I speak specifically of one kind of duplication: the need to
explain one decision in two places, so that you must change both
places if you change the decision, but the program does not
automatically detect if you forget and change only one place.
Programming consists largely of removing this kind of duplication.)

By the way, in Perl code I make my living from at present, we use an
assert function; it takes a lambda (called an anonymous sub in Perl,
and with some convenient syntactic sugar that makes this tolerable)
and a string.  I often wish it could print the source text of the
lambda, the way a Lisp assert macro can.  If lambdas in Perl
remembered their source text --- the way they did in ancient Lisps
like elisp --- the assert function could do this and still be just a
function.

(Pardon my pidgin elisp, but this works for lambdas without docstrings
 and with single body expressions:
  (defun myass (test) (if (not (funcall test)) (myass-err (caddr test))))
  (defun myass-err (obj) 
    (display-message "ASSERTION ERROR" (format "ASSERTION ERROR: %s" obj)))
  (myass (lambda () (equal (+ 3 3) 7)))
)

> Anecdotally, I went and grepped through our pretty large code base
> (the portions that are in Lisp), and was a bit surprised when I
> didn't see that many macros as I expected to. I'd say 60-70% could
> probably be replaced in-place w/ closure-style w/ no effect on the
> callees.. but the remaining might be a bit harder.

Lisp's lambda syntax might make that refactoring a painful burden on
the callers, although it might cost less in a language like Smalltalk.

As a Lisp novice, I'd love to hear about some of the tougher ones.  I
see 'loop', of course.

> Trevor -- Personally speaking, I've found the loss of macros really
> painful on those occasions when I've really needed them -- think "a
> deadline to get something up and running by 9 am tomorrow, and several
> pages of repetitive code to write, but no time to write an entire
> parser/rewriter/pre-processor/code-generator" :) (but I agree that I
> don't view macros as indispensable as say functions).

If I had twelve hours to get something up and running and several
pages of repetitive code to write, in, say, Perl, I'd spend an hour or
two writing a (Unix-style, string-based) preprocessor.  I imagine a
Lisp wizard could do the same thing much more quickly, since Lisp has
READ and WRITE.

-- 
<kragen@pobox.com>       Kragen Sitaker     <http://www.pobox.com/~kragen/>
Edsger Wybe Dijkstra died in August of 2002.  The world has lost a great
man.  See http://advogato.org/person/raph/diary.html?start=252 and
http://www.kode-fu.com/geek/2002_08_04_archive.shtml for details.