Also, macros, which are what (the view layer of) CLOS is built with. In most other object oriented languages, the language isn’t powerful enough to implement its own object system.
Having the full power of function calls in all cases (named or anonymous) is incredibly helpful. I know the Python party line is “just use def” but adding a line here and a line there adds up fast. A syntax for function objects is also great.
I’ll also call out the ability to use dynamic or lexical scoping on a per-variable basis. That has saved me hundreds of lines of work, and made an O(1) change actually an O(1) patch.
A proper numeric tower. Python has complex numbers, but they don't seem well integrated (why is math.cos(1+2j) a TypeError?). Fractions are frequently very useful, too, and Python has them, in a library, but "import fractions; fractions.Fraction(1,2)" is so much more verbose than "1/2" that nobody seems to ever use them.
Conditions! Lisp's debugger capabilities are amazing. And JWZ was right: sometimes you want to signal without throwing. Once you've used conditions, you'll have trouble going back to exceptions. They feel restrictive.
(I've come to accept that in a language with immutable data types, like Clojure, exceptions make sense. Exceptions feel out of place, though, in a language with mutability.)
Other big wins: keywords, multiple return values, FORMAT (printf on steroids), compile-time evaluation, a native compiler (and disassembler) with optional type declarations.
Lisp is unique among the languages I've used in that it has lots of features that seem designed to make writing large programs easier, and the features are all (for the most part) incredibly cohesive.
If you have multiple dispatch, then building/supporting a numeric tower is natural.
In your other reply you pointed out macros. They are a mixed blessing, easily misused. Other languages have them but use them more sparingly and making it harder to overlook their special status, which leads to better "code smell" in my opinion.
Do take a look at Julia. It has learned deeply from CL and innovated further.
Macros are easily misused, true, but so can any language feature. I can go on r/programminghorror and see misuses of if-statements. It's the classic argument against macros, and I hear it a lot, but I can't say I've seen it happen.
25 years ago, conventional wisdom said that closures were too complex for the average programmer, and today they're a necessary part of virtually every language. Could we be reaching the point where syntactic abstraction is simply a concept that every programmer needs to be able to understand?
I think "macros are easy to misuse" comes from viewing macros as an island. In some languages (like C), they are: they don't really integrate with any other part of the language. In Lisp, they're a relatively small feature that builds on the overall design of the language. Omitting macros would be like a periodic table with one box missing. It'd mean we get language expressions as native data types, and can manipulate them, and control the time of evaluation, but we just can't manipulate them at compile time.
That being said Julia's type system is definitely the way of the future in my opinion.
See here: https://wiki.python.org/moin/Why%20is%20Python%20a%20dynamic...
"objects can change their shape anytime" is a function of dynamic typing and is orthogonal to strong or weak typing.
Everything can be used as a bool. This was often used to check for None, but had some issues when used with - for example - datetimes which evaluated midnight as false. In part due to the fact the integer 0 evaluates to false.
Changing type unexpectedly is the key example given in your link (`"foo" + 3` is `"foo3"`). Meanwhile in python `foo.method()` can change the type of the variable foo. Which is a level of fuckery commonly found in javascript.
Let alone the fact that dynamic duck typing encourages weak typing over performing explicit conversions. This is embodied by "easier to ask forgiveness than permission" which says it's better to catch the type conversion exception than check if the type is an integer ahead of time. Which then leads to implementing javascript-esque add functions anyway.
I'll grant you python is stronger than some other dynamic languages, but it is still at least half an order of magnitude weaker than Julia, which is strong in ways approaching Haskell.
Unfortunately Typescript's type system is largely driven by the need to represent anything you can do in JS, for library compatibility reasons.
The main designer also wrote C# and Delphi. In a lot of ways, C#'s type system is better (less complex), but Typescript has the huge advantage of working with existing JS