In these essays, Paul Graham discusses programming languages and how their respective power can make or break your efficiency as a hacker. Across them he invariably arrives at the conclusion that Lisp is the greatest of all time and because of its flexibility no programming language can ever beat it. While his comments about Lisp’s flexibility especially about the language’s powerful macro system, I think his personal bias towards the language clouds his analysis, as he literally wrote the book on Lisp years before he founded Viaweb. I think the comparison between Lisp and Latin was insightful but also lacking. Lisp allowed Graham to interface with the system at a high “enlightened” level that few understand, but Latin never let you define new syntax as you spoke! I think the power and usefulness of Lisp macros may be overstated by Graham when viewed in a wider context. While they allowed him to define his own language as he wrote his startup, and he benefitted from the increased agility, it made his code vastly more difficult to understand as an outsider, causing Yahoo to rewrite his code in C++ and Perl. In this way, Viaweb terribly failed the bus test, because if Graham suddenly left the company before the rewrite, the product would be impossible to continue, at least at that velocity. This is why Google developed Go as a strongly opinioned language that, while powerful, makes you write your code in a certain way so that all Go more or less reads the same way. But still, as a startup they likely did not care about such resiliency, and clearly it ended happily for Graham who exited the company before it had to deal with such problems of scale.

Moving aside from Lisp, Graham makes many good observations about the importance of programming language choice. When he talked about the tradeoffs between convenience and performance in relation to Moore’s law, I immediately thought of Electron. Many purists, especially in the Linux community where Chromium hardware acceleration is not a given, will balk at the idea of shipping an entire web browser for a simple desktop app like Spotify. And yes, that is somewhat of an absurd thought. But those developers are taking Graham’s advice to prioritize ease and agility of development over being careful about performance and resource management. Ironically, this is mostly because everyone and their brother can program in the standard HTML/CSS/JS web stack (the opposite situation of Lisp at Viaweb), so why not just use it everywhere? In a world where Apple silicon and other incredibly efficient hardware dominates the market, the pointy-haired is indeed right to just use the industry standard. While there are some hold-outs like Apple, who pushes the native model of SwiftUI to build fast and efficient interfaces “easily” (its still not HTML…), even they have been caught sneaking some React into their applications because its just so damn easy.

So clearly there are more powerful languages than others. Onto the backend side, there is little reason to not use a modern garbage collected runtime like Node.js (V8), Python, C# (.NET), or Java (JVM). On the language side, Python and JavaTypeScript stand out as the most versatile, allowing you to easily import abstracted libraries like Express.js, Flask, and the suite of ORM tools to stand up a project quickly, whereas Java/C# require more careful architecture and carry more institutional cruft than these more nimble languages. On a personal note, I think JavaScript was one of the first languages I learned well enough for a major project about 7 years ago, and I was able to write so quickly because of the flexibility of the language. But this came with plenty of footguns - my lack of experience with the ORM led to many N+1 queries (towards a database across the internet) grinding the website to a halt and the dynamic typed nature of JS led to unpredictable behavior and random crashes. It has been a long time since I used JS extensively (briefly TS more recently) and I recently really like the balance that Python strikes with type checking to allow you to write code quickly, but also safely as you can incrementally add types to your code to give a better sense of safety in the long term. Still, I think Graham does not talk about tooling enough. When I have an IDE set up properly where I can easily debug my program without extra barriers, I think that gives me so much more agility in the “mid” programming languages than I would have otherwise. For that reason I think Graham’s comment that Lisp features that take 3 months to implement would take 5 years in the best case to implement in C++ is absolutely insane. Sure, if you compare a garage of Lisp wizards to a corporate prison of C++ 9-5ers doing the bare minimum, this could be a reality. But if you really want to compare the language itself and give it a fair shake, C++ programmers of the same caliper and armed with good tooling could accomplish the same task on a much more realistic timeline (although maybe not 3 months).

P.S.: Graham takes time to call out that this Python code:

class foo:
    def __init__(self, n):
        self.n = n
    def __call__(self, i):
        self.n += i
        return self.n

is less elegant than this Lisp code:

(defun foo (n)
  (lambda (i) (incf n i)))

Perhaps this just goes back to our earlier disagreement about resiliency, but I think the Python code captures the idea of the closure in a much more explicit and readable way than the Lisp code.