Is that CL, Clojure, a std Scheme or something? I really don’t have any issues jumping in either of these 3 even after a decade or more, but maybe if something obscure is used? And I wonder about the difference in naming and commenting as well then.
The cultists are all crying, "you suck at naming! you suck at comments!" And maybe I do! But those 12 people who will have to maintain it are going to suck even more. That's where a more "blub-like" language shines. It is enforcing a standard. And the cultists cry again, "i'm so smart i don't need blub to hold my hands!" Ok, if you say so... but where is your Minecraft (blub)? where is your Excel (blub)? where is your Facebook or your Google (blub)? maybe we can just accept that lisp is nice in some places, but not so nice for others?
And we are pro static types, it’s just easier to add them after I am done experimenting. Not upfront. And we do via various (backward compat) custom CL constructs and provers.
Because I find with any well designed and flexible program in a statically typed language, you end up with the same "tube of something" as you have in your dynamic language examples.
Take any large generic framework in rust, the types are extremely generic, dispatching on more generic traits. It's hardly a tag that says: "blood, to heart" unless it is highly coupled and working in one area.
Yet at some point, the template is instantiated--in the text!--and you can fill-in-the-blank by reading instead of having to do a runtime probe. For large systems that have a lot of preconditions, the "runtime probe" approach can be a thick fat time-waster (i.e. caressing the program into a state where something is even in the tube).
Because I find that in a good dynamic environment, it's easier to figure out how to use something complex when compared to trying to uncover how and why the types line up.
The absolute extreme being something like smalltalk, where you just guess how it should be used and then fix it in the debugger until it works.
No matter the tool, it will be very difficult to try out all code paths to figure out what can be in that tube. The difficulty scales with the size / age of the application.
Take your typical 20 years old enterprise application. Nobody from the original team is around. Whole generations of programmers worked on the project, each with their own favorite patterns / code style / level of diligence. There's a massive amount of features, half of which you don't even know about. Test coverage is spotty at best. You see some common function triggered from hundreds of places, it makes a huge difference in your ability to reason about it if you have types on the data being processed or not.
But only in theory. The shit that I keep seeing of incomprehensible spaghetti code in C and C++ disproves that theory on a daily basis.
http://www.aaronsw.com/weblog/rewritingreddit
"The others knew Lisp (they wrote their whole site in it) and they knew Python (they rewrote their whole site in it) and yet they decided liked Python better for this project. The Python version had less code that ran faster and was far easier to read and maintain."
Turns out, not once does the author actually give a reason why Python was better than Lisp for Reddit. The only references to that connection at all were claims that some “X” (all among the set that are oft-refuted by Lisp programmers) wasn’t the reason!
It reads more like a PR justification (“oh, there’s not much difference really, we even asked unspecified engineers who agreed with the change, and our code is working better and better!”) than an actual explanation.
The decision to write web.py instead of web.lisp was not elaborated on, however.
As mentioned above, the closest point I see to him discussing that decision is saying that ‘Python uses objects to make frameworks somewhat like the syntactic constructs Lisp allows you to make’, ie ‘you can do the same things as Lisp less easily in Python, so that’s not an advantage of Lisp’. Which is both incoherent as a refutation of the referenced reason to keep Lisp, and not a reason for the change to Python.
"THE PYTHON VERSION had less code that ran faster and was far easier to read and maintain."
I come from the opposite side and find python syntax and style stifling.
for example he dosent make any claim about the python implementation being "far easier to read and maintain" (even by proxy by claiming that CL was hard to read/maintain), and honestly starts out by absolutely singing CL's praises
his reasoning seems to boil more down to the ecosystem surrounding Common Lisps libraries being poorer than pythons:
> If Lisp is so great, why did we stop using it? One of the biggest issues was the lack of widely used and tested libraries. Sure, there is a CL library for basically any task, but there is rarely more than one, and often the libraries are not widely used or well documented. Since we're building a site largely by standing on the shoulders of others, this made things a little tougher. There just aren't as many shoulders on which to stand.
[etc...]
> So why Python?
We were already familiar with Python. It's fast, development in Python is fast, and the code is clear. In most cases, the Lisp code translated very easily into Python. Lots of people have written web applications in Python, and there's plenty of code from which to learn. It's been fun so far, so we'll see where it takes us.
We are still in that world. I do game programming, and am convinced that if a game programmer isn't aware of their memory allocations, then their game will drop frames. This is true today, even at 60hz (and things are moving towards 144hz, 240hz, and beyond). Reusing buffers is essential.
I think that being aware of when and where your memory comes from is the foundation of performance. The discipline of handling memory carefully tends to naturally lead toward making more performant decisions on everything else.
Separately, my gut feeling is that in 2024, programming language runtime technology is advanced enough that manually reusing buffers isn't necessarily a good use of hours-spent-optimizing. To pick on Python in particular, it _does_ actually reuse buffers when they only have a single owner; more languages might get this in the future via the Perceus refcounting work?
Typing being optional is not a problem. Like, if I'm looking at some function, and for some reason it's not clear what type of argument it's expecting, with one command I can pull up a cross-reference of other code that calls that function, and jump to it, and jump to the definition of the argument. If it's an object, I can jump to the definition of the class. This isn't really any more burdensome than jumping to the class in the first step, especially because in static langs I'll occasionally need to look at uses in context via cross-references anyway.
I don't get your dismissal of runtime probes. In large static systems, runtime probing is necessary for me to orient myself, the types don't really help. "How does the execution thread even get here?" is easily answered by sticking a break point there and running the thing, then you can inspect the stack. Common Lisp also has a very nifty "trace" function built-in, with a keyboard stroke I now get output every time the function is called along with what its arguments were and what its return values were. I can do this for any function, even built-ins, no instrumentation setup necessary or modifying the code or stopping/restarting the program. The full interactivity also means I can just jump to the definition even in some library-of-a-library code and if it pleases me rewrite it to add some prints or other info or just fix a bug and move that redefinition to my project. It's much easier for me to investigate a large Lisp system than a large Java system even though I've probably had more experience with the latter. Even grepping stuff is rather simple because of naming conventions.
Lisp definitely allows you to write code that is very easy to come back to and understand. I’ve written code like that, and I’ve seen code like that in eg certain sections of the SBCL internals, among many other projects.
However, there’s a reason we don’t write all our code in Python or Assembly. The fundamental semantic restrictions of one and the simple functionality of the other are both traded off against horrible scaling relative to a technical project’s complexity and scope.
If you write a project with complex stages/components in those kinds of languages (or mimic their styles in Lisp code, for that matter), either you have to split those components into parts that don’t make sense separately (making the code base significantly more complicated than eg an equivalent Lisp codebase), or you have to write extremely long and convoluted programs to accomplish conceptually simple functions (making the code base significantly more complicated than eg an equivalent Lisp codebase).
Similarly, if you use the Python or Assembly coding paradigms for large projects, you quickly get lost in a sea of individually simple-seeming code segments, unable to grok the connections between said segments just because of how many things need to be kept track of.
In contrast, for Lisp languages:
A) The default style used by Lisp programmers is highly functional, using many higher-order functions, using macros that solve their problem-domains without explicitly exposing the code doing the solving (objects also fall into this general role, btw, which is apropos given the Common Lisp Object System was defined purely with macros), and clearly delineating code / systems with mutating effects.
While this style is admittedly harder to grok than Python/Assembly for simple cases, it has almost no reduction in reading-complexity as code scale/complexity increases. Complexity gets naturally encapsulated and automated by functions/macros/objects (as opposed to either a restricted subset of those or simply the developer’s own head, which are the 2 approaches most other languages offer for complexity management) (algebraic type systems are a very cool exception to the prior note, but a pervasive object system (like CLOS) paired with macros allows you to make a type system (like Coalton) without feature/ergonomics loss).
B) Interactive development and ergonomic DSL creation are objective wins for complex or large programs; the former significantly reduces iteration time for both cases, while the latter makes it easy to just write code once that addresses the complexity/scale of the domain and then never deal with that complexity again (or at least not with the full complexity, even if you don’t understand the domain well enough to obviate the irrelevant complexity entirely in your DSL).
Sure, hot-reloading in a few other languages gives us most of the former (although things like CL conditions, debugging REPL-layers, access to object contents & stack-frame inputs (only addressed by some debuggers, and only in a restricted way), and some other features still lack equivalents) and other languages generally use objects alone to address (most instances, though not all, of) the latter (rather than Lisp’s mix of macros/objects/higher-order-functions) (we’re ignoring the text-templating macros in languages like C++ because everyone agrees they’re more trouble than they’re worth). But Lisp is still by far the most featureful and ergonomic offering of both of these facilities.