| There are languages that have useful features that lisp lacks. For example, Haskell has lazy evaluation and automatic compiler generated program correctness proofs (a decent type system). Then there are languages that try to be even more powerful than Haskell, for example Epigram, which has dependent types. Lazy evaluation is probably the language feature that is most mainstream that lisp lacks. For those not familiar with the abstraction benefits of lazy evaluation, consider a library for dealing with prime numbers. In a language without lazy evaluation, you are going to need an API with a function for getting the n_th prime number, a function for testing if a number is a prime number, a function for getting the smallest prime number larger than the number x, and a bunch of other functions. In Haskell, the API only needs a single variable, called "primes" that is simply the infinite list of all the prime numbers: primes :: [Integer] -- primes is of type list of Integer Thanks to lazy evaluation, this single variable is all that is needed to support all of the above operations in an efficient way. For example, to get the n_th prime number you simply write (primes !! n) Now, this may look cool, but what does it have to do with abstraction? Since "primes" is a regular list, I can use it with any list function and build more complex things out of it. I can also have a list of all the catalan numbers in the same way, and all my functions that I've written that do cool things with the prime numbers can now be instantly used against catalan numbers. You can manually do lazy evaluation in languages that don't natively have it, but unless you do it everywhere you won't get the abstraction benefits, and if you do do it everywhere then the compiler almost certainly will not be able to optimize it as well as a native implementation. |
What I see breaking in a lisp are the language features that come from limitations in the language. There's two ways to look at a language's power: What it enables you to do directly, and what it enables you to do by taking things away (in the form of constraints that are enforced on your code) and what it builds on top of that.
Looked at the second way, there are powerful things that can be built in some languages that Lisp is actually less-well suited for, because it is practically and philosophically opposed to constraints. Haskell's type system is actually the thing I see that would be the hardest to implement in Lisp. Haskell's type system allows the type system to say not just what a function returns, but in many ways, exactly what the function does and how it does it. If you have a function of type Int -> Int, then you know, with only a shadow of a doubt (unsafePerformIO), that the function does no IO, or, in fact, anything else except somehow manipulate an Int. (And as the doubt's name implies, you are taking your fate into your hands if you use that.)
Upon this foundation of constraints, Haskell can build in some features that Lisps can not, such as its safe implementation of STM. It's not that Lisp couldn't have something like a STM system, it's that it lacks the ability to make and use such strong guarantees about what is in an STM transaction that Haskell could, and I think you end up seeing a "reversion to the mean" effect where if the constraints are not enforced, they end up violated both accidentally and deliberately.
Similarly, almost every cute trick the Haskell type system allows, while certainly abstractly doable in Lisp, is not enforcable and therefore abstractions further layered on top of that are correspondingly less safe.
It is a viable opinion to say that you trust your programmer and that you feel that you should not care about such things; I'm not actually advocating these features in this post. (I'm still ambivalent myself, still gathering the data to have an opinion.) It's just that unless I'm very much mistaken, it's effectively impossible to get guarantees about the properties of closures passed in to your code in Lisp in the way that Haskell does. (You can examine the code before executing it, but along with the general tediousness of trying to prove these properties, I wouldn't be surprised you run afoul of Rice's theorem. It certainly seems unlikely it could be practical.)
When features are built on doing powerful things in arbitrary combinations, Lisp is one of the go-to languages. When you have features that have to be built on restrictions in the language (and not trivial ones, but tricky ones), Lisp has a problem. (Again, unless I'm very much mistaken.)
I don't think Haskell "blubs" Lisp, but I think it makes a very good case that the ordering of language power doesn't have a single apex in Lisp. Lisp may dominate in the "more power" arena, but that's not the only arena around.