6 ms·
C is not good. At least, not in the way parent means “good”. C is this brilliant awful crazy-successful hack, yak-shaved into existence just to bootstrap an en
by hhas01 7y ago
C is not good. At least, not in the way parent means “good”.
C is this brilliant awful crazy-successful hack, yak-shaved into existence just to bootstrap an entire brand new OS (UNIX) in record time; subsequently Peter-Principled into domains it really has no business being in (basically anything userland). This is why, fifty years on, we’re still fighting memory corruption and security holes and generally doing an awful job of parallel computation and everything else C is not very good at.
Everyone should learn C, if only to understand where we’ve come from, where we are now, and where we should be trying to go in future.
Languages like Lisp and Forth and Smalltalk are fundamentally different beasts to Algol’s descendents.
Most developers, having being raised in a pure C/Algol culture, give no thought to the base complexity and limitations inherent in those languages; they just assume that is how computation is.
What the Lisps do is reveal all that complexity and limitations to be artificial constructs, imposed by the designers of those languages, whether as convenience to implementation (as in C, where features like flow control statements map closely to underlying machine instructions) or simply because that’s what they’re already used to (c.f. 90s scripting languages like Python and JavaScript, born on the back of mature, ingrained C culture).
..
Here’s a question to get you thinking: Why do Algol family languages have hardwired flow control statements? Not because that’s how programming needs to be, but because that’s how they’ve always done it. Often there’s some special-case behavior involved, e.g. deferred evaluation of operands, as in the case of conditional (`if`) statement bodies, and hard-wiring that particular special-case into the language guts is a quick way of addressing that need.
Heck, even traditional Lisps (1 & 2) are guilty of hardwiring hackery (don’t let its uniform syntax fool you: Lisp is ridden with special forms, including conditionals). You have to go to something like John Schutt’s Kernel language to see that problem solved correctly, by extracting the special-case behavior (lazy evaluation) into a general, reusable feature. Schutt calls it “vau”, but it’s just a way to dictate how and when arguments should be evaluated at the language level, instead of baking that behavior into the language implementation where it can be neither modified nor reused.
What’s the upshot? Well, first, you realize that 90% of what Algol languages treat as core features aren’t “core” at all. They can be pulled out, generalized, and expressed as plain old composable functions.
Suddenly your “core language” collapses from something big and complicated, with dozens of special-case features baked in, each with its own special syntax and behavioral rules, down to something ridiculously simple: an evaluator, a mechanism for composing behaviors (functions with vau support), and maybe some primitive datatypes to get you started (although even those can be pushed out into libraries if you want), … and that’t it. It is the mother of all refactorings, and it reveals the simple truth.
All those features and complexity the Algols had us believe were essential prerequisites to computation and programming… all gone, pushed out to external libraries which the user can freely import, modify, replace, or ignore as they need. Once you realize how small, simple, and elegant computation can (and should) be, you’ll wonder what the hell all that vast intricate pomp and ceremony is really for, and who it really serves.
Programming languages are big and complicated not because that’s how they need to be but because nobody tries to make them small and simple. But as Tony Hoare said:
“There are two methods in software design. One is to make the program so simple, there are obviously no errors. The other is to make it so complicated, there are no obvious errors.”
So if a language already falls in the latter camp, then Dog only imagine all the software subsequently written in it!
This is where C falls outwith its original purpose, and it’s telling that subsequent popular attempts to fix it have done so by adding even more complexity on top, e.g. C++, Java, Swift. (It’s only recently with the likes of Rust that trend has started to reverse, but Rust is still a Complex language compared to a Forth or a Smalltalk.)
..
Now, there is a tendency in Lisp circles to rave about macros as if they’re the True Revelation, but they’re not. The revelation is when you realize you have the ability to reshape a language you are given into the language that you need; a language that precisely and concisely expresses the concepts and behaviors that are of interest and relevance to you in your particular problem space. Lispers describe this as “bottom-up programming”, but the term hardly does it justice.
Find yourself writing “if not TEST do ACTION” a lot? Extract that out into your own first-class “unless TEST do ACTION” command and use that going forward. Want to evaluate commands non-sequentially according to your own [e.g. priority-based] rules? Done. Don’t want loops? Leave them out.
When you write an “Algol” program, the language always controls how your program is evaluated. If you don’t like the way it does it, tough. When you write a “Lisp 3” program, you get to control how your program is evaluated. You aren’t stuck with a fixed language-level set of first-class flow control statements that may or may not meet your needs, plus a weak second-class user-level mechanism for kind of creating your own (C-style functions, with their inherent limitations). There’s only one level: everything is a first-class feature, whether it’s provided by the language or added by you.
..
We often think of C as a “general-purpose language”, but I think this is wrong and unhelpful. We should really think of C as a DSL, one created specifically for bit-twiddling and crude algorithmic number crunching (which is what it’s good at). The true general-purpose languages are the meta-languages, because they have the power to be whatever we want them to be.
Working in C, you always speak in C. In a “Lisp 3”, you can speak in any language you like; you just have to grow that initial minimalist foundation of key building blocks into the language you want, and then off you go. It is the difference between Algorithmic Thinking and Compositional Thinking.
Yes, algorithmic thinking has achieved a lot in the last 50 years, but the software we’ve built that way has a lot of problems—safety, reliability, scalability, learnability, verifiability—that I strongly suspect are inherent to that approach. We really need to try compositional thinking to escape that trap, but we can’t even start to do that while the languages we commonly use (Algol family) are themselves fundamentally crap at compositionality.
--
Lastly, I think Nile (https://news.ycombinator.com/item?id=19844088 https://news.ycombinator.com/item?id=19844088) requires an honorary mention in parent’s list; not because of the language itself but because of how it’s constructed: not only is it a composed language, but the language it’s written in is a composed language too. If that doesn’t melt your mind at the possibilities, nothing will!
- SaxonRobber 7y agoTLDR; I’d sooner dig up SML’s grave than be caught near a Lisp.
- hhas01 7y ago“Standard ML”? Exposure to Lisp is useful, not in that it’ll suddenly make you want to use Lisp yourself but in how it makes you realize just how crushingly, squalidly “Plato’s Cave” the entire Algol-descended language world really is. And disaffection is the first step toward change and improvement; just don’t expect those comfortable in their chains to welcome it.
- bitwize 7y agoThe problem with "you can roll your own language constructs" is that, er, everybody wants to roll their own language constructs -- as opposed to simply using a library of common ones that have been tested and proven stable and useful. That's what Algol-like languages give you. They free you from the pain of having to roll your own language and let you get on with developing applications. As an added bonus, developing to a single commonly accepted language spec means your boss can read the code and figure out what it's doing without having to understand the entire code base at once. (Ever been a neophyte on a Rails project? Multiply that bewilderment by like a thousand.) That's why they're so accepted in business.
- BubRoss 7y agoI think that is much more a symptom of not having good core libraries built into the language in the first place. Programs written in modern C++ tend to make heavy use of the STL data structures. They aren't perfect, especially the maps, but they are solid and rarely a bottleneck. If you look at random numbers, time, the filesystem and even iostreams, there are plenty of rough edges to talk about it you want, but most people don't try to do their own thing in those areas because most programs don't need it at all. People will fill in gaps, so it depends on what gaps are left and how big they are.
- hhas01 7y agoAlgol languages give you a set of constructs that are the expressive equivalent of banging two rocks together. I would hope that good programmers, given grown-up building blocks, would use them to express concepts a bit more sophisticated than that. Your argument is a common one, but it’s founded on a flawed premise: that Algol-language users know what they’re doing. Sure, they can read and write code written in that language, but how many of them actually understand the problem domain in which that software ostensibly solves problems. Truth is, a lot of programmers today are simply faking it: they don’t understand the business; what it does, why it does it, why it does it the way it does, and what the problems are that it has doing it. They know how to write code, and that’s all they know; or are interested in knowing. For that type of programmer, rolling language constructs for the sake of rolling language constructs is simply a way to make themselves appear busy and productive without actually having to produce anything of value. Because to write useful code they’d have to learn the user’s business first—and that’s exactly what they want to avoid, because they hate having to learn stuff they’re not interested in and really don’t want to do it. Take away the Lisp and give them an Algol; won’t make a blind bit of difference. They’ll still churn out the same endless makework, only this time it’ll be expressed as vast convoluted pointless class hierarchies and reams of autogenerated boilerplate. (You mention Rails: Worst Offender Ever.) Idiots will write useless code in any language. Business accepts that type only because it doesn’t realize how bad it’s being scammed by them; which often as not is because the managers responsible for running that business are an absolute bunch of know-nothing bullshitters themselves. .. The only people who will benefit from Lisp expressiveness are those who appreciate that the language itself and the code written in it is the least important part of the whole process. Because what they’re interested in is understanding and solving the users’ problems, and if they have in their toolkit the ability to construct a language that talks in the language of the business itself then so much the better for solving it. You don’t have to be a great programmer to do this, and do it successfully too. (I’ve done it, and I’m a bear of very little brain.) You just need to understand the business itself; to get in the shoes of the folk who do those jobs day-in day-out and walk around in them till you see their world as they do. Once you can speak the language of that business well enough to “pass”, making a machine speak that language too is NBD. And once the machine speaks it, well, there you go. Cos that’s not a language that bangs two rocks together. That’s a language that lays a six-lane freeway in fresh-baked tarmacadam, all painted and ready to roll. Code is not the product. Code is just tedious crap you have to wade through on your way to the product. As a developer, nothing pleases me more than not having to write code; or at least no more code than is absolutely unavoidable. .. Those Nile links are good ones, I do recommend following them up. How to write a graphics pipeline in a hundred lines of code. Imagine if every program we wrote was like that, even programs that currently run to tens or hundreds of thousands of lines in those familiar “safe” Algol languages. That’s what we should be shooting for in this profession, cos I dunno about you but I’d rather learn to read a hundred lines of code written in a custom language that’s tailored to that domain than 10,000 lines in a lowest-common-denominator crapfest like C++.