1. The condition system including restarts to recover from errors.
2. A uniform syntax that can be fluently manipulated with paredit-style modes.
3. VM image saving/loading.
4. Runtime access to the compiler and very complete reflection.
5. A package system (ASDF) that lets you dynamically recompile and reload an entire system with all its dependencies just by calling a function.
6. First-class namespaces.
7. Well-defined multiple dispatch.
8. Metaobject protocol.
9. Language specification with conceptual clarity.
10. High-quality optimizing compilation despite extreme dynamicity.
I think the list could go on for a while...
A lot more work has to go into guaranteeing reproducible builds, and it tends to be a fairly non-compact representation of your executable. If CL were a little bit less customizable at load-time, that would allow for much faster FASL loading, which in turn would make the reliance on saved-images less important.
Obviously there are uses for that feature, and having a feature doesn't by itself make a language worse, but when that feature is a square-peg, but still the best fit for a particular round-hole, it causes problems.
More extreme case lies in the Lua/OpenResty world, they used to have some interactive debugger but since really so few people are using it, the debugger just got abandoned. Everyone is just accustomed to print-based debugging.
BTW: I actually agree that #3 can be quite useful, I'm using pry quite extensively when I'm working on Ruby projects. But I guess different communities might just have different preferences.
But you are right. Different strokes for different folks.
This is especially true for Ruby(and maybe for Clojure?), since in many cases documentations for libraries are not so great, so you will really have to try the library a little bit to get to the results you want.
[edit]
I also recommend that you watch the video xuejie posted (https://www.youtube.com/watch?v=4hfMUP5iTq8), as it's about using a debugger to write code more than using a debugger to fix code.
Sounds interesting, could you expand or point me in the right direction? Do you mean this https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node80.html ?
For example, the `+=`-operator found in many languages is implemented as a macro in CL (INCF). It needs to read the value of a place, increment it and then set the new value to the same place.
Doing that with a variable is easy. Using non-lisp syntax
x += 5
expands to x = x + 5
Doing that with object slots is easy too, if you don't mind accessing the slot directly. However, if you want to have getter and setter methods (which may not even correspond directly to a slot in the object) the expansion would look different. o.setFoo(o.getFoo() + 5)
or if the getter and setter can have the same name o.foo(o.foo() + 5)
Since the expansion is different, the macro would have to figure out whether it's dealing with a variable or an accessor method and behave differently. With generalized references you can use the accessor method as if it was a variable o.foo() // calls the getter
o.foo() = 5 // calls the setter
Now you easily can expand o.foo() += 5
to o.foo() = o.foo() + 5
just the same as you would with a variable. Behind the scenes you still have separate getter and setter methods, but the macro doesn't need to worry about that.Perl 6 has that and a lot more of the goodies (multiple dispatch, etc), from parent and the brother comments.
But sure, I'd love to find a job where I can use Lisp or start with Scheme.
I definitely agree that with each passing year, languages that are not recognizable as being related to lisp have more features that mad lisp special in the past.
Features I regularly use that are good, but aren't "normal features of any modern language" (some are implementation details, but common to most CL implementations)
1 CLOS
2 Homoiconicity
3 A good interactive debugger with incremental compilation
4 Generalized references
5 An equivalent to reader macros
In addition, while some form of AST parsing and manipulation may exist in many modern languages, it is typically much less accessible than in LISP. I think this is largely because of the lack of homoiconicity, but also partly a culture issue.
There are probably other features that I don't use that others make good use of as well.
As a last comment, it baffles me that #3 is something that is extraordinarily rare to find outside of the lisp, smalltalk and forth families, but is regularly mentioned as a huge productivity boost by programmers that use it.
There's actually no reason I can think of that non-pure statically typed languages couldn't have such tooling either, with the restriction that global variables (including functions) never change type.