The state of the Japanese economy is largely due to their financial services sector, overregulation by the government, and their massive debt. Underperformance of their technology and engineering firms is not what is contributing to their economic state. Ergo, your question is largely irrelevant to the point at hand.
Moreover, the Japanese economy is recovering quite nicely now, again thanks to better performance from their financial services sector as well as steps by Koizumi and his party to remove government regulation.
That's an asnine comment. If learning a tool allows you to use a more powerful technique to solve you problems, what's the problem with that? Heck, by your logic, FORTRAN is a better language than Haskell, because you don't even have to learn how to type to use it --- just put holes in a punchcard!
You do realize that the precedent in US case law is that the bar for "slander" is much higher for politicians than for regular people? It's almost impossible to slander a politician, and its intentionally that way.
The enumeration in the Constitution, of certain rights, shall not be construed to deny or disparage others retained by the people. "
Historical factoid: One of the original reasons there was resistance to the Bill of Rights was because many believed that enumerating certain rights in the Constitution would make people presume that any rights not so enumerated did not exist.
You misunderstand my point. I'm not suggesting an implementation that doesn't retain dynamic behavior. I'm pointing out that using that dynamic behavior has a very significant cost, and is not really equivalent performance-wise to the macro mechanism presented earlier. Sure, even in Lisp, you *can* do what the OP suggested and build the classes up at runtime. However, using this feature greatly reduces the ability of the compiler to perform static analysis on the program. Utlimately, it makes no sense to pay this performance price in the particular example we're talking about. Conceptually, the class hierarchy is known at compile time. It's not created in response to dynamic conditions, but is something known in advance. The language should be expressive enough to let you specify this succinctly, and not force you to do something unnaturally in the context of your problem domain just to reduce the number of primitives necessary in the language! Languages should be designed for programmers to use, not the other way around! There is a reason Lisp has both macros and dynamic classes.
You are, of course, right. I didn't mean to imply that the LispM was "bad engineering", merely that in this day and age, with the types of processors we have, it wouldn't be performance-competitive to have a "Lisp chip". Ultimately, its a question of optimizing your performance by drawing the hardware | compiler line in the right place. As the nature of hardware and of compiler technology changes, that line will move around. It seems to me with clock-speeds hitting a wall, and hardware designers having more transistors than they know what to do with, that line may shift back towards hardware a bit. I'll wager that Intel and AMD *will* in the not too distant future eventually support pointer tag bits in hardware. With everything moving to.NET and Java as it seems to be doing, improving GC performance won't just be a niche goal. Moreover, stuff like indirect branch prediction, which is now being done to make up for the latency of accessing main memory, will help things like generic dispatch in Lisp.
Loop is awesome! You're just not smart enough to figure it out:)
The point about Python doing the same thing as your example is crap. I'm a Python programmer, and yes, you can do what he's saying. But it's a dynamic hack that works only because in Python objects are just hash tables, and all member resolution is done at runtime via table lookup with a string key. It's *slow*, prevents you from doing *any* type inference, any optimization, or any type checking.
I just don't think this extra tLisp echnical strength actually empowers the programmer in real-world scenarios.
That wasn't your original argument.
If all macros are replaced with functions, then suddenly it is possible to do all those things.
You'll have to not only replace everything with functions (meaning that you basically need a Lisp, since any non-functional syntax will cause problems with your method), but you'll have to replace them with functions that are designed to be extended. Writing every function with the intent to be extended is non-trivial. With macros, you can write functions naturally, and still have the power of extension.
I think every problem I've seen solved by Lisp is solved at least almost as adequately, and in almost all cases, more simply and with a clearer syntax, by Python.
I'd love to see a domain-specific macro language implemented in Python. You can say that DSLs are "just syntax", but by that argument, assembly is fully equivalent to both Python and Lisp!
That's why Python does have a special syntax for assignment+naming in the same time.
More special syntax. That's exactly what Lisp avoids! Why bother with special syntax for everything when macros can be used instead? There is a reason that Lisp *does not* have a special syntax for assignment+naming at the same time. It'd be redundant, macros can be used to build that functionality out of existing infrastructure.
I can read programs in almost any language with virtually no learning curve, but I can say reading Lisp code, and defmacro's is much more of a challenge, despite knowing the concepts very well.
Translation: "I've got a lot of experience reading infix languages, but very little reading Lisp". You're basically saying: "I've got lots of experience reading English, but Japanese is hard to read, despite knowing the grammer very well". It's a stupid statement!
Lisp programs are not, as you would expect according to the claims you stand behind, orders of magnitude smaller than the equivalent Python, Smalltalk or Ruby programs.
Lisp programs can be an order of magnitude smaller than the equivalent Python program. Python is quite expressive, but I'd like to see something like ACL/2 in Python!
Out of order execution is there to wring parallelism out of basically single-threading languages. It's not an x86 issue, but a RISC one as well. The most massively OOO CPU in existance (the POWER4) is a RISC chip. In fact, the only significant RISC chip that is not OOO is the SPARC.
The basic point you're missing is that macros can extend *anything*. It doesn't matter that CLOS is implemented using macros, it could be built-in to the language or even a closure-based multimethod system. Macros can extend it anyway. However, the closure-based system can only extend functionality that is both closure based and designed to be extended in a fine-grained manner. This situation makes macros strictly more powerful than functions taking closures.
I think that useful manipulations of this type should not be a function of the actual content of the function, but manipulations of the function object, and of what happens before/after the function.
Well, you're free to think that, but that's not the point. You say that functions taking closures are equivalently powerful to macros. If one can do things the other cannot, then that's not true, is it?
Push is really something that the sequence types must support.
Again, that's not the point! If the language already does what you want, then that's easy. But macros aren't needed to solve those easy things, they are needed to solve harder things! Eg: you can use macros to write domain-specific languages. Sure, it'd be easier of Lisp supported all possible DSLs to begin with, but that's not a great idea, is it?
This presupposes that your language allows you access to the symbol table at runtime. In Lisp, most named function references are resolved at compile-time. Again, if you assume features the base language doesn't have, then you can always come up with a rebuttel. But the point is that macros are a single construct that can extend the base language, while here you are constantly inventing base language functionality for each thing that can be handled by macros!
Again, look at Smalltalk. Such a macro can be implemented as a set of functions that take a closure.
I'd love to see someone try to implement CL's LOOP as functions taking a closure. You can get the same functionality, of course, but the synactical contortions would be non-trivial.
Other languages have a different syntax for the creation of builtin data types, for assignments, for attribute getting/setting, etc. This helps programmers read the program.
In Lisp, you have functions that do this (and your editor highlights them!). SETF, MAKE-INSTANCE, SLOT-VALUE, etc, stand out just as much to a Lisp programmeras =, new, and . stand out to a C++ programmer.
Lispers seem to miss the fact, that when millions of students are exposed to C++ or Java, they keep on using them after university. While when they are exposed to Lisp, they remember it as some really bad memory, a language they would never touch again.
Millions of students are exposed to *Scheme* in school. They are also exposed in an educational setting, where the professor (for your own good), makes you rewrite MAP, etc, doesn't let you use macros, etc. That explains part of the bad memory. The other part is that students don't know enough programming at that point to know what is useful and what is not. My counter-example is that comp.lang.lisp has many people who are experts in C++ or Java. When you've dealt with C++ codebases reaching into hundreds of thousands to millions of lines, you learn to appreciate Lisp.
You missed the point. I don't care about GVR's multmethod implementation --- I'm using CLOS. Since you say that functions taking closures are equivalent to macros, you should be able to write a function that emulates my macro.
It's easy to extend something that was designed to be extended in particular ways. That's not what you need macros for. You need macros to define constructs that aren't planned-in to the base code to begin with.
What? Are you seriously arguing that a mildly risque advertisement is "unprofessional"? What color is the sky on your planet? More importantly, on your planet, what do "Gap" ads look like?
And please learn the English language. "Dapper" doesn't mean gay, it means stylish!
Okay, here is an example I've encountered in my own code.
Say you've got a generic method A, and two types B and C. B and C are not related in the type hierarchy (because there is no logical relationship between the two), but as far as A is concerned, they are both handled the same way. In Lisp, I used a simple macro to extend defmethod to allow me to specify multiple matching subclasses. The macro would expand to multiple defmethods, each specializing on one subclass, but containing the same body. Basically, I could write:
(defmethod-multi A ((arg (B C)))...long boring code using arg...)
No other code had to be aware of this implementation trick. They could just call A as usual, and get the expected result.
Try achieving the same thing with a function that takes a closure. Hell, try this:
; extend a list with another list (defmacro push-extend (value lst)
`(setf,lst (append,lst,value)))
BS. Take a look at the die of something like an Athlon 64. Large portions of it are dedicated to C support. For example, the entire MMU. If C programs (more appropriately, the C memory model) didn't allow programs to write willy-nilly all over memory, you'd save enough die space to put a couple of more execution units on there. Consider also the massive amounts of out-of-order execution hardware. If C wasn't built on the model of a unit-width instruct stream, a lot of that hardware could be dispensed with.
Programmers seem to have this idea that "C works the way CPUs work". Nothing could be farther from the truth. Modern CPUs are out-of-order, parallel vector machines. The C machine model assumes in-order, single-pipeline scaler machines. C assumes a linear memory space, while modern machines have complex cache hierarchies and often have non-uniform main memories. There are a lot of elements of functional languages that map better to modern hardware than does C. For example, in (strict) functional languages, the following two statements can be executed in parallel:
a = makea() b = makeb()
A compiler for such a language can map these statements directly to two commmand streams issued to seperate execution units. A C compiler can do something similar, but has to go through massive dataflow analysis to ensure that makea() doesn't modify anything used in makeb(). In another example, functional languages tend to encourage the use of lots of small, transient objects, while C encourages accessing large, long-lived global data structures. Guess which one is more suited to modern machines, with small-fast caches, and large-slow main memories?
And that's just how hostile C is to modern hardware. Don't even get me started on how hostile its "every pointer can overwrite every object" model is hostile to mdoern compilers!
The LispM model is an obsolete one. Its bad engineering to make a "fast Lisp computer", when you can make a "fast computer" and run a "fast Lisp" on it. The advantages you mentioned are largely irrelevent in modern RISC designs.
1) Why bother with bounds-checks in hardware when, in a language like Lisp, you can have the compiler insert the bounds checks? With modern machines with several integer pipelines, some of which are usually idle, its not like the cost of a bounds check is noticible.
2) There is some validity in this --- it would be nice for hardware to provide tag bits so integers and floats wouldn't have to be twiddled before doing arithmatic on them, but type-checking can be handled just fine in software. Again, when you've got several integer pipelines, the cost of bounds check is not that big.
You're not solving the basic problem that macros solve, however. Receiving a function as a closure doesn't allow you to manipulate the function. You can use it as-is, but a lot of very powerful constructs require that manipulation. Many of the macros used in Common-Lisp programs could not be rewritten as a function taking a closure. A trival example is PUSH. Bigger examples are DEFUN, DEFCLASS, DEFMETHOD (pretty much all of CLOS, really), etc. Your loop example is particularly good. Languages like Python rely on hard-coded syntaxes for their looping constructs, which invariably express only a very limited subset of the looping constructs the programmer wants to use. Something like CL's LOOP macro is far more powerful (and wisely used, can be every-bit as readable), but could not be implemented as a function taking a closure.
As for "giving up a readable syntax", I consider Lisp's much more readable than C++. Lisp is readable if you're used to reading Lisp. Its unreadable if you're not. The same is true of C++. Before anybody complains about Lisp's syntax being unreadable, they should have at least a thousand lines of code (in an actual program, not some homework assignment!) under their belt. A thousand lines seems like a lot, but its a decent metric for seperating those who know Lisp and those who do not. Ask yourself: would you hire somebody who claimed to "know" C++, when they hadn't written more than a thousand lines in it?
No, I'm saying that your average C/C++/Java programmer is perfectly happy living with all the problems of his tools, just as long as he doesn't have to learn anything new or anything that doesn't have enough hype behind it. It's kind of like the "nobody ever got fired for buying IBM" type mentality. As long as nobody else is willing to push the boundaries either, everybody is fine!
To actually appreciate Lisp or Dylan, you have to actually program in it. You have to put the kind of effort you put in to learning C or C++ or Java, perhaps more because you have to unlearn all the things you need to do in those languages, but don't need in a good language. Very few people are willing to make that jump, and take that risk. Very few of the people who do can ever look back to C/C++/Java and find them acceptable. Its interesting to see just how many people on comp.lang.lisp have been using C/C++/Java for years, but now prefer Lisp. You'll find almost nobody on comp.lang.java who has been using Lisp for years and now prefers Java!
Go Virginia! Between Mark Warner and this guy, I've never been prouder to be from the "Land of Dixie".
PS> Yes, we do have signs on our border that say "Welcome to the Land of Dixie".
The state of the Japanese economy is largely due to their financial services sector, overregulation by the government, and their massive debt. Underperformance of their technology and engineering firms is not what is contributing to their economic state. Ergo, your question is largely irrelevant to the point at hand.
Moreover, the Japanese economy is recovering quite nicely now, again thanks to better performance from their financial services sector as well as steps by Koizumi and his party to remove government regulation.
That's an asnine comment. If learning a tool allows you to use a more powerful technique to solve you problems, what's the problem with that? Heck, by your logic, FORTRAN is a better language than Haskell, because you don't even have to learn how to type to use it --- just put holes in a punchcard!
Yeah, I meant 'pure', not 'strict'. I just couldn't think of the word at the time...
You do realize that the precedent in US case law is that the bar for "slander" is much higher for politicians than for regular people? It's almost impossible to slander a politician, and its intentionally that way.
It's because it's in the 9th amendment:
"Amendment IX
The enumeration in the Constitution, of certain rights, shall not be construed to deny or disparage others retained by the people. "
Historical factoid: One of the original reasons there was resistance to the Bill of Rights was because many believed that enumerating certain rights in the Constitution would make people presume that any rights not so enumerated did not exist.
You misunderstand my point. I'm not suggesting an implementation that doesn't retain dynamic behavior. I'm pointing out that using that dynamic behavior has a very significant cost, and is not really equivalent performance-wise to the macro mechanism presented earlier. Sure, even in Lisp, you *can* do what the OP suggested and build the classes up at runtime. However, using this feature greatly reduces the ability of the compiler to perform static analysis on the program. Utlimately, it makes no sense to pay this performance price in the particular example we're talking about. Conceptually, the class hierarchy is known at compile time. It's not created in response to dynamic conditions, but is something known in advance. The language should be expressive enough to let you specify this succinctly, and not force you to do something unnaturally in the context of your problem domain just to reduce the number of primitives necessary in the language! Languages should be designed for programmers to use, not the other way around! There is a reason Lisp has both macros and dynamic classes.
You are, of course, right. I didn't mean to imply that the LispM was "bad engineering", merely that in this day and age, with the types of processors we have, it wouldn't be performance-competitive to have a "Lisp chip". Ultimately, its a question of optimizing your performance by drawing the hardware | compiler line in the right place. As the nature of hardware and of compiler technology changes, that line will move around. It seems to me with clock-speeds hitting a wall, and hardware designers having more transistors than they know what to do with, that line may shift back towards hardware a bit. I'll wager that Intel and AMD *will* in the not too distant future eventually support pointer tag bits in hardware. With everything moving to .NET and Java as it seems to be doing, improving GC performance won't just be a niche goal. Moreover, stuff like indirect branch prediction, which is now being done to make up for the latency of accessing main memory, will help things like generic dispatch in Lisp.
Loop is awesome! You're just not smart enough to figure it out :)
The point about Python doing the same thing as your example is crap. I'm a Python programmer, and yes, you can do what he's saying. But it's a dynamic hack that works only because in Python objects are just hash tables, and all member resolution is done at runtime via table lookup with a string key. It's *slow*, prevents you from doing *any* type inference, any optimization, or any type checking.
Where did you find a card that can handle 100M polygon scenes with HDR lighting and fully-procedural textures, at 100fps at 1600x1200?
I just don't think this extra tLisp echnical strength actually empowers the programmer in real-world scenarios.
That wasn't your original argument.
If all macros are replaced with functions, then suddenly it is possible to do all those things.
You'll have to not only replace everything with functions (meaning that you basically need a Lisp, since any non-functional syntax will cause problems with your method), but you'll have to replace them with functions that are designed to be extended. Writing every function with the intent to be extended is non-trivial. With macros, you can write functions naturally, and still have the power of extension.
I think every problem I've seen solved by Lisp is solved at least almost as adequately, and in almost all cases, more simply and with a clearer syntax, by Python.
I'd love to see a domain-specific macro language implemented in Python. You can say that DSLs are "just syntax", but by that argument, assembly is fully equivalent to both Python and Lisp!
That's why Python does have a special syntax for assignment+naming in the same time.
More special syntax. That's exactly what Lisp avoids! Why bother with special syntax for everything when macros can be used instead? There is a reason that Lisp *does not* have a special syntax for assignment+naming at the same time. It'd be redundant, macros can be used to build that functionality out of existing infrastructure.
I can read programs in almost any language with virtually no learning curve, but I can say reading Lisp code, and defmacro's is much more of a challenge, despite knowing the concepts very well.
Translation: "I've got a lot of experience reading infix languages, but very little reading Lisp". You're basically saying: "I've got lots of experience reading English, but Japanese is hard to read, despite knowing the grammer very well". It's a stupid statement!
Lisp programs are not, as you would expect according to the claims you stand behind, orders of magnitude smaller than the equivalent Python, Smalltalk or Ruby programs.
Lisp programs can be an order of magnitude smaller than the equivalent Python program. Python is quite expressive, but I'd like to see something like ACL/2 in Python!
Out of order execution is there to wring parallelism out of basically single-threading languages. It's not an x86 issue, but a RISC one as well. The most massively OOO CPU in existance (the POWER4) is a RISC chip. In fact, the only significant RISC chip that is not OOO is the SPARC.
The basic point you're missing is that macros can extend *anything*. It doesn't matter that CLOS is implemented using macros, it could be built-in to the language or even a closure-based multimethod system. Macros can extend it anyway. However, the closure-based system can only extend functionality that is both closure based and designed to be extended in a fine-grained manner. This situation makes macros strictly more powerful than functions taking closures.
I think that useful manipulations of this type should not be a function of the actual content of the function, but manipulations of the function object, and of what happens before/after the function.
Well, you're free to think that, but that's not the point. You say that functions taking closures are equivalently powerful to macros. If one can do things the other cannot, then that's not true, is it?
Push is really something that the sequence types must support.
Again, that's not the point! If the language already does what you want, then that's easy. But macros aren't needed to solve those easy things, they are needed to solve harder things! Eg: you can use macros to write domain-specific languages. Sure, it'd be easier of Lisp supported all possible DSLs to begin with, but that's not a great idea, is it?
def defun(name, func):
func.func_name = name
caller_namespace()[name] = func
return func
This presupposes that your language allows you access to the symbol table at runtime. In Lisp, most named function references are resolved at compile-time. Again, if you assume features the base language doesn't have, then you can always come up with a rebuttel. But the point is that macros are a single construct that can extend the base language, while here you are constantly inventing base language functionality for each thing that can be handled by macros!
Again, look at Smalltalk. Such a macro can be implemented as a set of functions that take a closure.
I'd love to see someone try to implement CL's LOOP as functions taking a closure. You can get the same functionality, of course, but the synactical contortions would be non-trivial.
Other languages have a different syntax for the creation of builtin data types, for assignments, for attribute getting/setting, etc. This helps programmers read the program.
In Lisp, you have functions that do this (and your editor highlights them!). SETF, MAKE-INSTANCE, SLOT-VALUE, etc, stand out just as much to a Lisp programmeras =, new, and . stand out to a C++ programmer.
Lispers seem to miss the fact, that when millions of students are exposed to C++ or Java, they keep on using them after university. While when they are exposed to Lisp, they remember it as some really bad memory, a language they would never touch again.
Millions of students are exposed to *Scheme* in school. They are also exposed in an educational setting, where the professor (for your own good), makes you rewrite MAP, etc, doesn't let you use macros, etc. That explains part of the bad memory. The other part is that students don't know enough programming at that point to know what is useful and what is not. My counter-example is that comp.lang.lisp has many people who are experts in C++ or Java. When you've dealt with C++ codebases reaching into hundreds of thousands to millions of lines, you learn to appreciate Lisp.
You missed the point. I don't care about GVR's multmethod implementation --- I'm using CLOS. Since you say that functions taking closures are equivalent to macros, you should be able to write a function that emulates my macro.
It's easy to extend something that was designed to be extended in particular ways. That's not what you need macros for. You need macros to define constructs that aren't planned-in to the base code to begin with.
1) The parens aren't clear because its the editor that handles the parens, not the programmer.
2) Linear thinking is mildly retarded when all lexically-scoped languages are tree-structured.
3) I'd really like to see you try.
4) What?
What? Are you seriously arguing that a mildly risque advertisement is "unprofessional"? What color is the sky on your planet? More importantly, on your planet, what do "Gap" ads look like?
And please learn the English language. "Dapper" doesn't mean gay, it means stylish!
Okay, here is an example I've encountered in my own code.
...long boring code using arg...)
,lst (append ,lst ,value)))
Say you've got a generic method A, and two types B and C. B and C are not related in the type hierarchy (because there is no logical relationship between the two), but as far as A is concerned, they are both handled the same way. In Lisp, I used a simple macro to extend defmethod to allow me to specify multiple matching subclasses. The macro would expand to multiple defmethods, each specializing on one subclass, but containing the same body. Basically, I could write:
(defmethod-multi A ((arg (B C)))
No other code had to be aware of this implementation trick. They could just call A as usual, and get the expected result.
Try achieving the same thing with a function that takes a closure. Hell, try this:
; extend a list with another list
(defmacro push-extend (value lst)
`(setf
BS. Take a look at the die of something like an Athlon 64. Large portions of it are dedicated to C support. For example, the entire MMU. If C programs (more appropriately, the C memory model) didn't allow programs to write willy-nilly all over memory, you'd save enough die space to put a couple of more execution units on there. Consider also the massive amounts of out-of-order execution hardware. If C wasn't built on the model of a unit-width instruct stream, a lot of that hardware could be dispensed with.
Programmers seem to have this idea that "C works the way CPUs work". Nothing could be farther from the truth. Modern CPUs are out-of-order, parallel vector machines. The C machine model assumes in-order, single-pipeline scaler machines. C assumes a linear memory space, while modern machines have complex cache hierarchies and often have non-uniform main memories. There are a lot of elements of functional languages that map better to modern hardware than does C. For example, in (strict) functional languages, the following two statements can be executed in parallel:
a = makea()
b = makeb()
A compiler for such a language can map these statements directly to two commmand streams issued to seperate execution units. A C compiler can do something similar, but has to go through massive dataflow analysis to ensure that makea() doesn't modify anything used in makeb(). In another example, functional languages tend to encourage the use of lots of small, transient objects, while C encourages accessing large, long-lived global data structures. Guess which one is more suited to modern machines, with small-fast caches, and large-slow main memories?
And that's just how hostile C is to modern hardware. Don't even get me started on how hostile its "every pointer can overwrite every object" model is hostile to mdoern compilers!
You have to be careful with that comparison. How much is emacs itself? About 500,000 LOC. How much is written in elisp? Considerably more.
Not when you use it inside Texmacs, it doesn't. Powerful math capabilities, great TeX output, what more could you want?
The LispM model is an obsolete one. Its bad engineering to make a "fast Lisp computer", when you can make a "fast computer" and run a "fast Lisp" on it. The advantages you mentioned are largely irrelevent in modern RISC designs.
1) Why bother with bounds-checks in hardware when, in a language like Lisp, you can have the compiler insert the bounds checks? With modern machines with several integer pipelines, some of which are usually idle, its not like the cost of a bounds check is noticible.
2) There is some validity in this --- it would be nice for hardware to provide tag bits so integers and floats wouldn't have to be twiddled before doing arithmatic on them, but type-checking can be handled just fine in software. Again, when you've got several integer pipelines, the cost of bounds check is not that big.
You're not solving the basic problem that macros solve, however. Receiving a function as a closure doesn't allow you to manipulate the function. You can use it as-is, but a lot of very powerful constructs require that manipulation. Many of the macros used in Common-Lisp programs could not be rewritten as a function taking a closure. A trival example is PUSH. Bigger examples are DEFUN, DEFCLASS, DEFMETHOD (pretty much all of CLOS, really), etc. Your loop example is particularly good. Languages like Python rely on hard-coded syntaxes for their looping constructs, which invariably express only a very limited subset of the looping constructs the programmer wants to use. Something like CL's LOOP macro is far more powerful (and wisely used, can be every-bit as readable), but could not be implemented as a function taking a closure.
As for "giving up a readable syntax", I consider Lisp's much more readable than C++. Lisp is readable if you're used to reading Lisp. Its unreadable if you're not. The same is true of C++. Before anybody complains about Lisp's syntax being unreadable, they should have at least a thousand lines of code (in an actual program, not some homework assignment!) under their belt. A thousand lines seems like a lot, but its a decent metric for seperating those who know Lisp and those who do not. Ask yourself: would you hire somebody who claimed to "know" C++, when they hadn't written more than a thousand lines in it?
The moral of this post? We should all be programming in BASIC!
No, I'm saying that your average C/C++/Java programmer is perfectly happy living with all the problems of his tools, just as long as he doesn't have to learn anything new or anything that doesn't have enough hype behind it. It's kind of like the "nobody ever got fired for buying IBM" type mentality. As long as nobody else is willing to push the boundaries either, everybody is fine!
To actually appreciate Lisp or Dylan, you have to actually program in it. You have to put the kind of effort you put in to learning C or C++ or Java, perhaps more because you have to unlearn all the things you need to do in those languages, but don't need in a good language. Very few people are willing to make that jump, and take that risk. Very few of the people who do can ever look back to C/C++/Java and find them acceptable. Its interesting to see just how many people on comp.lang.lisp have been using C/C++/Java for years, but now prefer Lisp. You'll find almost nobody on comp.lang.java who has been using Lisp for years and now prefers Java!