Lisp programmers tend to think that every problem is best solved by layering abstractions. Macros, homoiconicity, quasi-quoting, and s-expressions facilitate abstraction. Some even think programming is abstracting.
But abstracting has a cost. Sometimes in terms of performance (perhaps mitigated with great effort or a sufficiently smart compiler), but always in terms of cognitive load. A elegant lisp expression is meaningless in isolation. When reading lisp code, you must recursively find and remember the definitions for each token on the page before you can understand what it does. Hopefully the author knew Naming Things is Hard and choose good names. Hopefully the names point you in the right direction. But you can't be sure unless you traverse each definition to its leaves. Lisp programmers are blind to the power and clarity of thought that comes from direct expression - all definitions visible at once, with no indirection.
A lisp programmer might look down on a Java programmer's reliance on IDEs. An IDE is a powerful tool for shoveling mountains of code, but a lisp programmer might say "I can solve this problem without mountains of code". Likewise an apl programmer might look at a lisp solution and say "I can solve this problem without defining any new terms". A forth programmer might say "I can solve this without parentheses". A C programmer might say "I can solve this without a heap". An assembly programmer might say "That entire program is equivalent to a single x86-64 instruction".
By crafting different bespoke DSLs for each new problem, lisp programmers lose the opportunity to hone one DSL to perfection. The knowledge that any problem could be solved with lisp lures them toward the fallacy that every problem should be solved with lisp.
> Lisp programmers tend to think that every problem is best solved by layering abstractions.
Nah, when I write Lisp programs, I have more of an appreciation for appropriate abstractions, not needless abstractions.
> A elegant lisp expression is meaningless in isolation.
Elegance is usually in relation to something. For example,
(list 1 2 3)
Is more elegant than
var list = new LinkedList<Integer>();
list.add(1);
list.add(2);
list.add(3);
> When reading lisp code, you must recursively find and remember the definitions for each token on the page before you can understand what it does.
You need to do this for any language. In Java and many other languages, having a "Go to definition" IDE function is very useful.
> Lisp programmers are blind to the power and clarity of thought that comes from direct expression - all definitions visible at once, with no indirection.
Not sure what this is referring to. Do you have an example?
> A lisp programmer might look down on a Java programmer's reliance on IDEs.
Plenty of Lisp programmers use Emacs, which has a great many tools to help developers including jumping to definitions, showing documentation, running and using a step debugger for code, etc. Not sure why Lisp programmers would look down on Java programmers because of an IDE.
> By crafting different bespoke DSLs for each new problem, lisp programmers lose the opportunity to hone one DSL to perfection.
This seems to be a Lisp meme. Just because Lisp can be used to make a DSL doesn't mean all Lisp programs are macro-implemented DSLs. There's plenty of Lisp code that looks just like Java code with function, struct, object definitions and calls, except it uses parens.
Fair, but if your language keeps growing, this task is never done.
> Not sure what this is referring to. Do you have an example?
Array programmers sometimes avoid abstractions because they prefer "idioms" like these[1]. So rather than curate a library of words like:
barchart: {x>\:!|/x}
and have the programmer use the word "barchart", they instead prefer to use the definition itself. The word "barchart" has a specific meaning (here, an ascii "bar chart" of 0s and 1s, showing the relative sizes of the values of input array x), but "{x>\:!|/x}" might be useful for more than just bar charts. This idiom contains smaller idioms like "count til max" (!|/) which in turn contains "max" (|/).
Being able to see the code makes it easier to explore and tweak to your specific needs. But more importantly, there are no "official" names for concepts like "count til max". That's just my personal name for it. A python programmer would call it "range". You could come up with your own name for (!|/) that makes perfect sense to you. But that name will probably be longer than its definition, and less flexible.
Ok, but Lisp doesn't force you to curate a library of words or require dealing with abstractions.
Maybe this example is relevant. Racket defines procedures `filter` and `map` for `list`. Also provided is `filter-map`, which I assume may satisfy your not-being-direct-expression concern about Lisp. But, `filter-map` exists for a particular reason:
> Like (map proc lst ...), except that, if proc returns #false, that element is omitted from the resulting list. In other words, filter-map is equivalent to (filter (lambda (x) x) (map proc lst ...)), but more efficient, because filter-map avoids building the intermediate list.
So it exists for performance concerns. It also exists in the "Additional List Functions and Synonyms" so it's not like it's being confused for a core, important function like `filter` or `map`. I still write Racket code where I just explicitly have something like:
The thing is, a compiler could easily recognize the pattern (filter identity (map proc lst ...)) and rewrite it for efficiency (perhaps by using filter-map).
Writing it that way in the first place reduces the verbosity of the code, making it look better.
But abstracting has a cost. Sometimes in terms of performance (perhaps mitigated with great effort or a sufficiently smart compiler), but always in terms of cognitive load. A elegant lisp expression is meaningless in isolation. When reading lisp code, you must recursively find and remember the definitions for each token on the page before you can understand what it does. Hopefully the author knew Naming Things is Hard and choose good names. Hopefully the names point you in the right direction. But you can't be sure unless you traverse each definition to its leaves. Lisp programmers are blind to the power and clarity of thought that comes from direct expression - all definitions visible at once, with no indirection.
A lisp programmer might look down on a Java programmer's reliance on IDEs. An IDE is a powerful tool for shoveling mountains of code, but a lisp programmer might say "I can solve this problem without mountains of code". Likewise an apl programmer might look at a lisp solution and say "I can solve this problem without defining any new terms". A forth programmer might say "I can solve this without parentheses". A C programmer might say "I can solve this without a heap". An assembly programmer might say "That entire program is equivalent to a single x86-64 instruction".
By crafting different bespoke DSLs for each new problem, lisp programmers lose the opportunity to hone one DSL to perfection. The knowledge that any problem could be solved with lisp lures them toward the fallacy that every problem should be solved with lisp.