Why I still Lisp
&gt; Languages based on the λ-calculus make it really easy to “play back the code” in your head.<p>This really depends on the expression.. I&#x27;ve seen behemoth expressions such that you have no choice but write down intermediate results, or start explicitly breaking it down into sub-bindings so you could step through the code. And yes, you can usually step through subexpressions in the debugger, but if you have to debug anyway, it hardly matters that you can &#x27;play back&#x27; the code in your head.<p>I also noticed there is a tendency in lispy languages to avoid introducing variable bindings just for the sake of naming the subexpression, because it often comes with a `(let ((name (subexpression))) rest-of-code)`, which also results in extra tabulation for the rest of the body. Compare with variable introduction in languages like python&#x2F;js&#x2F;c++&#x2F;rust, etc., where such thing wouldn&#x27;t cause touching the rest of the function&#x2F;clause. I guess it&#x27;s more of a &#x27;functional language&#x27; artifact, e.g. in Haskell you&#x27;d probably have a similar issue.<p>The simple rules don&#x27;t remove the complexity, the evaluation state has to live somewhere.<p>&gt; I have never had a static type checker (regardless of how sophisticated it is) help me prevent anything more than an obvious error (which should be caught in testing anyway).<p>Err, really? Never dereferenced a null pointer, or tried using a list instead of a set? Static types <i>are</i> testing, it doesn&#x27;t replace it, but massively saves mechanical work on writing dozens of dumb tests. Having tests is very important for documentation, regressions, end-to-end&#x2F;integartion testing, but every test for something stupid as &#x27;throws on passing null&#x27; is just a time drain (unless you&#x27;re launching rockets or something of course).<p>Anyway, it&#x27;s possible to have the best of both worlds by using gradual typing like mypy or JS flow -- you get all the runtime benefits (flexibility&#x2F;ability to temporary violate invariants) and you gradually harden code which makes sense to type. I really wish Elisp (the lisp I&#x27;m mostly dealing with) had some sort of types, at least for simple things list &#x27;associative list&#x27;, &#x27;string&#x27;, &#x27;function reference&#x27;, &#x27;nullable thing&#x27;, this would massively save me time on catching bugs. It has runtime type checks for `defcustom` things, but never seen type checking anywhere else (except for occasional runtime asserts).<p>&gt; The biggest advantage of this form of syntax is a form of minimalism — you don’t need spurious syntactic constructs to convey concepts.<p>&gt; call these things macros, or syntactic extensions. In other words, you can extend the syntax of your language to introduce new abstractions.<p>I find these two sentences a bit contradictory ;) Anyway, I personally like macros, if used sparingly it can really help. 
One great upside of s-expressions for me is that you can do some cool things like &#x27;find and replace&#x27; for whole subexpressions (for monkey patching third party code, for example). I use `el-patch` [0] in my emacs config and `advice-patch` for surgically changing the default behaviors of some org-mode functions to compile my blog [1].<p>That said a similar sort of thing is possible, for example, in python with `patchy` [2], and perhaps many other languages? But I guess it&#x27;s not as organic as in lisps, e.g. `advice-patch` implementation is less than 100 LOC, whereas in case of python you have to rely on existing heavy lifting done by `ast` module.<p>As of simple syntax, it really gets in the way sometimes, e.g. I&#x27;m always annoyed by constant quoting in Elisp because the same type of brackets (only `()`) is used. In comparison, in Clojure it&#x27;s much more readable with (), [], {} (and more).<p>&gt; Lisp is not an interpreted language. It is not slow<p>A bit of nitpicking, but... <i>which</i> Lisp? :) Anyway, these days it&#x27;s often meaningless to say &#x27;slow&#x27; without having a specific workload in mind and having done benchmarking; hardware improvements make it very hard to reason about.<p>&gt; all implementations come with lots and lots of levers to tweak performance for most programs. In some cases the programs might need assistance from faster languages like C and C++ because they are closer to the hardware, but with faster hardware, even that difference is becoming irrelevant.<p>Well, this is true of most languages.<p>[0] <a href="https:&#x2F;&#x2F;github.com&#x2F;raxod502&#x2F;el-patch#el-patch" rel="nofollow">https:&#x2F;&#x2F;github.com&#x2F;raxod502&#x2F;el-patch#el-patch</a><p>[1] <a href="https:&#x2F;&#x2F;github.com&#x2F;karlicoss&#x2F;beepb00p&#x2F;blob&#x2F;a4fd7cb95e1705412d68c568ea13f2b0a831527f&#x2F;exobrain&#x2F;src&#x2F;publish.el#L45-L74" rel="nofollow">https:&#x2F;&#x2F;github.com&#x2F;karlicoss&#x2F;beepb00p&#x2F;blob&#x2F;a4fd7cb95e1705412...</a><p>[2] <a href="https:&#x2F;&#x2F;github.com&#x2F;adamchainz&#x2F;patchy#patchy" rel="nofollow">https:&#x2F;&#x2F;github.com&#x2F;adamchainz&#x2F;patchy#patchy</a>