syntactic abstractions dont scale up with the number of programmers
... as an objection to Lisp macros. It has become the invariable staple of these discussions and is accepted (by the people who accept it) without any question. Yet is there any evidence for it? I don't think I've ever even seen any evidence alleged for it.
The fact is, to justify this statement requires more than proving it's true. You'd have to prove that this failure of syntactic abstraction to "scale up with the number of programmers" is worse than the failure of abstraction in general to do so. Indeed, in my experience, it's programming that fails to scale up with the number of programmers.
Heres what Dijkstra has to say about something that is tangentially related (he is talking about the importance of read time comprehensibility of code). "My second remark is that our intellectual powers are rather geared to master static relations and that our powers to visualize processes evolving in time are relatively poorly developed. For that reason we should do (as wise programmers aware of our limitations) our utmost to shorten the conceptual gap between the static program and the dynamic process, to make the correspondence between the program (spread out in text space) and the process (spread out in time) as trivial as possible." This is from the influential GOTO considered harmful paper.
- Edsger Dijkstra, CACM, 15:10
I don't have time to watch the video, but it seems rather obvious that the Dijkstra quote has little to do with macros. What he's saying sounds to me like "lexical scoping is easier to understand than dynamic scoping". If anything, that makes macros more valuable rather than less, since macros are intrinsically lexical and macroexpansions don't typically change over time.
Or, to put it another way: lots of C++ shops ban the use of advanced templating metaprogramming. Has C++ suffered egregiously as a result?
How does the complexity of C++ templates prove anything about syntactic abstraction in general or Lisp macros in particular?
Also, K has a "whizbang sleek look" that makes Lisp look ridiculously verbose. (http://www.nsl.com/papers/kisntlisp.htm) Ditto J and APL, though K seems to take it furthest. Part of their density comes from being based on ideograms, not lexical tokens; the density is like comparing Chinese to English.
Unfortunately, K is closed source and limited to 32-bit platforms for non-commercial evaluation. It's also quite expensive, since its primary product is a mind-bogglingly fast database designed for real-time stock data analysis. I'm been fascinated with K for a while, though, albeit mostly at a distance. (J's evaluation isn't limited to noncommercial use, though.)
Is there a support group available? If the three books you read when the kids are asleep are The Reasoned Schemer, The Art of Prolog, and On Lisp, are you now officially beyond help on the language geek scale? I used to be a somewhat productive programmer. These days I think more about making sure I've picked the right language for the problem . . .
I do most of my actual programming in Lua + C and an in-house language. I really like Erlang (but haven't worked on the right kind of projects lately), and I also prototype stuff in Prolog.
I would love to have a more modern dialect of Prolog, designed primarily for embedded use as a C library (much like Lua, and ideally with a similar C FFI). It's a cool language, but it's also really strongly skewed towards certain problem domains. Lua has taught me that when a language can delegate its weak points to a symbiotic language, it can stay really small and focused on its strengths.
Perl, a slow language criticized for its syntax and bearing the stigma of being a mere scripting language, is often lauded as having huge community support and tons of helpful (even if often crappy) libraries via CPAN. C++ has similar support (no CPAN that I am aware of, but I have always quickly found solutions and libraries with the search engine).
Granted, CL can connect via U/CFFI to any external library, but it is harder to find the wrappers for CL than C/C++ header files.
It's just that they're done the UNIX way, with a small miniature compiler or XML substrate, instead of the Lisp way, where you write everything in S-exprs. This tends to make the little languages easier to grok for users but harder to implement, which tends to be a good tradeoff, as there will be far more users than implementers of a programming language.
I totally agree that good development shops do lots of compile-time metaprogramming. The issue is whether they do enough when dealing with problems (that start out as) too small to justify writing a full blown parser/compiler, and if they don't, how much of the shortage is attributable to a lack of language support in C++/Java.
To clarify what I mean, I have presented lisp ideas to fellow coworkers and met with resistance along two lines:
(1) They want to understand what is really going on, and the abstraction can obscure that. This sounds to me like a general abstraction problem more than an issue with syntactic abstraction... and really, it seems like a problem with understanding how someone chose to abstract things. They want to look underneath the hood for answers.
(2) They suggest that new developers will have a hard time understanding and adding to the system. But new developers for any project have to learn the local language anyway. It may look like C++, python, or whatever, but it the framework other developers developed always has its own learning curve.
Surely syntactic abstraction is just another abstraction tool that can be used to help make those frameworks easier to dive into. There is always poor code and poor documentation; this is not an excuse not to declare functions or other pretty approaches. Why is it any scarier to use this tool than to use someone's library?
That is a good point on the macroexpand functions; I would hope that if syntactic abstraction was added to C++, there would be a similar way to view the resulting code (sounds like a great future gdb / MSVC debugger feature).
In Haskell, I can only think of one very narrow use for them which has never actually come up for me in practical use (automatically generating instances for combined monads).
Out of curiosity, can anyone provide a more common use of macros which can't easily be done in Haskell with HOF/Lazy evaluation?
So you're right, lazy evaluation can do just about anything in a purely functional environment. But now count the number of programming languages that provide a purely functional environment...
The rest of us need macros.
I can think of a couple of examples where I've done such things in lisp (mostly elisp, sometimes Clojure), but I don't see a good reason why I couldn't do them in Haskell. In fact, a function f: a -> M d (with M = IO, Writer or State if you want side effects) almost serves the same purpose: it turns a value into a set of actions to perform.
I'm really curious, could you give an example of such a task/macro?
(Note: I'm not trying to engage in a language war, I'm honestly curious. If I'm treating Haskell as Blub, I'd like to know about it.)
"Greenspun's Tenth Rule of Programming: any sufficiently complicated C or Fortran program contains an ad hoc informally-specified bug-ridden slow implementation of half of Common Lisp."
- Philip Greenspun
"Including Common Lisp."
- Robert Morris
"We were not out to win over the Lisp programmers; we were after the C++ programmers. We managed to drag a lot of them about halfway to Lisp."
- Guy Steele, Java spec co-author