That has no basis in reality. Smalltalk is to implement that one implementation fits in the Smalltalk-80 book.
C got popular because it allowed low-level code to be implemented in something higher-level than assembler and moderately portably too (machines back then where as lot more dissimilar than today). It's hard to disassociate C from Unix, the former making the latter easy to port. A symbiotic relationship.
It's important to remember C was designed for the processors of the time, whereas processors of today (RISC-V included) are arguebly primarily machines to run C. C has brought a lot of good but also a lot of bad that we are still dealing with: unchecked integer overflows, buffer under- and overflow, and more general memory corruption.
No ISA since the SPARC even tries to offer support for non-C semantics.
> C got popular because it allowed low-level code to be implemented in something higher-level than assembler and moderately portably too (machines back then where as lot more dissimilar than today). It's hard to disassociate C from Unix, the former making the latter easy to port. A symbiotic relationship.
You mean like Burroughs was doing with Extended Algol in 1961 for its Burroughs B5500 system, implemented with zero lines of Assembly, while offering all the features for memory safety that C designers deemed too hard to implement?
Archeology of systems programming languages is great to dismitify the magic aura C seems to have gained in the last 20 years with the raise of FOSS.
"Extended Algol"? So, not Algol, then. Algol with the parts that would be in assembly language turned into libraries?
And FullyFunctional's point was largely about portability. So, if Burroughts took that OS and tried to port it to, say, a PDP-11, how well do you think that would have worked?
Just like it is impossible to have an ANSI C library without Assembly or language extensions. Extended Algol had what we would call intrisics nowadays.
> Just like it is impossible to have an ANSI C library without Assembly or language extensions. Extended Algol had what we would call intrisics nowadays.
Sure. It makes your "zero lines of assembly" claim somewhat less impressive, though...
"In fact, all unsafe constructs are rejected by the NEWP compiler unless a block is specifically marked to allow those instructions. Such marking of blocks provide a multi-level protection mechanism."
"NEWP programs that contain unsafe constructs are initially non-executable. The security administrator of a system is able to "bless" such programs and make them executable, but normal users are not able to do this."
"NEWP has a number of facilities to enable large-scale software projects, such as the operating system, including named interfaces (functions and data), groups of interfaces, modules, and super-modules. Modules group data and functions together, allowing easy access to the data as global within the module. Interfaces allow a module to import and export functions and data. Super-modules allow modules to be grouped."
Sounds familiar? Rust like safety in 1961, but lets praise C instead.
This is my one big beef with the RISC-V ISA, after I went over it with a fine toothed comb, otherwise it's brilliant to my untutored eyes, and the ISA doc, a paper I read about the same time about how difficult it was to make ISAs super fast, etc. helped explain why, e.g. the VAX ISA took so long to make faster, and was probably doomed, while Intel got lucky? with x86.
Anyway, the large integer math e.g. crypto people are not happy about it, the lack of support for anything other than being able to check for divide by zero means their code will be slow compared to older ISAs that support it. And their justification in the ISA doc is that the most popular languages nowadays don't check for this, which is not the sort of thing I want to see for an ISA that's billing itself as a big thing for the future. I very much got Worse Is Better/the New Jersey vibes from this....
Intel didn't get "lucky". Going superscalar (and OoO) with CISC ISA's like the VAX, IBM/360, x86, ... obviously isn't impossible (as illustracted by IBM's zNext and Intel's everything), but it's VERY difficult (thus hard to design and verify => time to market risks) and expensive (it takes a lot of logic and more pipeline stages). An ISA like RISC-V tries to avoid as much as _practical_ of these obstackles, thus making it easier & cheaper for implementations to be fast. This part is straight out of the DEC Alpha playbook.
Re. large integers: the RISC-V 2.0 (non-priv) ISA is frozen solid so any change would have to be an extension. I've been gently pushing for the mythical vector extension to be at least friendly to large integers.
I've seen no evidence that C brought us anything good. Anything I can do with C I can do with a Modula-2-like language with better safety and compiler robustness. If a check is a problem, I can disable it but it's there by default. Nonetheless, I'll look into any benefits you think C brought us that alternatives wouldn't have done.
The alternative to C for systems programming in the '70s was not Modula-2 or anything like it, the only realistic alternative for most production systems was writing your kernel code in assembly. C allowed the industry to finally stop defaulting to assembly for systems programming. That was a very good thing, regardless of how old and inappropriate C might be for such purposes nowadays. The comment you were responding to reads to me like someone providing historical context, not an expression of an ideal.
What extension was that? Burrough's ESPOL (the notable use of an Algol extension for system programming, on a processor designed to be an Algol target) had what we would now call inline assembly.
> Mesa was already being used at Xerox PARC when C was born.
Close; C ~ 1972 (started 1969), Mesa ~ 1976 (started 1971). The Alto system software was mostly BCPL; Mesa arrived with the commercial D series. Mesa also targeted a virtual instruction set (i.e. like Java or p-code) designed specifically for it, run by an interpreter on the bare metal.
ALGOL68: The language Thompson, Ritchie, and Pike basically cloned when they made Go. Nothing went in there unless everyone agreed on it. Final was every feature they had wanted in a language. Most of which was already in ALGOL68 with key aspects in use by Burroughs since their 1961 designs. Had they just subseted or simplified ALGOL68, they'd have quite the head start and we'd be stuck with a language that's a lot better than C. I imagine the implementation would've been more suitable for OS development, too. ;)
The point was that those machines had the benefit of instruction sets co-designed with the language. (And Mesa had strong default type safety, but basically the same memory model as C, allowing pointer arithmetic, null pointers, and dangling pointers.)
One of the things about C was: American Telephone and Telegraph Company was a regulated monopoly and prohibited from selling anything outside of the telephony business. So they developed C and Unix but couldn't sell either commercially. However they could allow universities to use both for free. Deal with C was one grad student could write a cross compiler in about 300 hours. And then you could use that to cross compile Unix and the compiler.
End result a lot of CS students learned C in school.
Other languages tended to be tied to a particular machine, cost money, or weren't really suitable (Fortran/p-system Pascal) for the stuff people wanted to do.
"The alternative to C for systems programming in the '70s was not Modula-2 or anything like it"
The main alternatives were ALGOL60, ALGOL68, and Pascal. They were each designed by people who knew what they were doing in balancing many goals. They, esp ALGOLS, achieved good chunk of most. A subset and/or modification of one with gradual progression for improved hardware would've led to better results than C with same amount of labor invested. On low end, Pascal ended up being ported to systems from 8-bitters to mainframes. On high end, Burroughs implemented their mainframe OS in an ALGOL with hardware that enforced its safety for arrays, stacks, and function calls.
In the 80's, things like Modula-2, Concurrent Pascal, Oberon, and Ada showed up. I'll understand if they avoided Ada given constraints at the time but the others were safer than C and quite efficient. More importantly, their authors could've used your argument back then as most people were doing but decided to build on prior work with better design. They got better results out of it, too, with many doing a lot of reliable code with very little manpower. Hansen topped it off by implementing a barebones, Wirth-like language and system called Edison on the same PDP that C was born on.
"The comment you were responding to reads to me like someone providing historical context, not an expression of an ideal."
The historical context in a related comment was wrong, though. It almost always is with C because people don't know it's true history. Revisionism took over. The power that revision has in locking in people to C is why I always counter it with actual evidence from the inventors of the concepts. Two quick questions to illustrate why I do that and test if it's sensible:
1. When hearing "the programmer is in control," did you think that philosophy was invented by Thompson for C or it was stolen without early credit from BCPL along with its other foundations?
2. Did you know that those were not designed or validated as a good language at all? And that they were grudgingly accepted just to get CPL, an ALGOL60 variant, to compile on crappy hardware whose problems no longer exist by chopping off most important attributes?
If you knew 1 and 2, would you still think C was a "well-designed" language for systems programming? Or a failed implementation of ALGOL60 whose designers were too limited by hardware? If No 1, we should emulate the heck out of C. If No 2, we should emulate ALGOL60-like language that balances readability, efficiency, safety, and programming in the large. Btw, all the modern languages people think are productive and safer lean more toward ALGOL more than C albeit sometimes using C-like syntax for uptake. Might be corroboration of what I'm saying.
I actually mostly agree with you. My point was poorly made. I didn't mean that C was particularily great, only that it "won" and having a winner meant more portable code.
I'll back out of the PL debate; this isn't really the place and there's too much to be said on the topic than will fit in this margin.
> [...] or it was stolen without early credit from BCPL along with its other foundations?
That's simply not correct. The first public paper on C (The C Programming Language in BSTJ #57, the Unix issue) spent the first 4-page section on its BCPL ancestry.
Show me where it references BCPL. As I see it, Ritchie only references B as the inspiration for C. From the document, anyone who hadn't read the B paper (most people) would think they, Thompson, and/or Ritchie mostly came up with C's features and strengths. They eventually became known as the "C philosophy" and C-like languages.
Whereas, it was Martin Richards that invented both of those plus the idea of stripped down languages for writing tools on minimal hardware. He should get credit for creating the "BCPL philosophy" that the "programmer is in control" plus inspiring a BCPL-like language called C that allowed UNIX's success. Instead, early work focused all attention on Thompson and Ritchie with UNIX papers/software making sure that was the case by adding momentum.
At least I got to see the guy in action in the video linked to my History of C page. It shows his team, the constraints they worked on, why they dumped most of ALGOL, and him working with it later on. That presentation put things into an honest perspective where I can see his and UNIX founders' contributions to overall success with due credits (and/or critiques) to each.
No, investment in compilers for a language brought that. There were numerous languages at various points in our history that could've received that compiler work. C's design-level issues actually make it harder to write optimizing compilers for it that don't break the correctness. So, my claim is that it's the hardware, software, and groups using it that brought C's success (and compiler investments) along rather than the language itself.
While everything you say is possibly true, the original statement was that C didn't bring anything good. I believe the evidence of it's usage disagrees with that statement.
Also, on optimization, I don't know exactly what you mean by that. I assume you mean b/c it's got a lot of undefined behavior. There is so much optimization going on in tools like LLVM that I question it's accuracy.
Anyway, I agree that there are more pleasurable and portable languages (and now just as performant/lightweight with Rust).
There are numerous barriers to optimization that mostly fall into the categories of a) too many operations break continuity of typing and data flow, and b) many small, independent compilation units.
For example: Pointers, casts, unions, casting pointers to different types, and doing all of this across procedure call boundaries all challenge/break data flow analysis. You can pass a pointer to a long into a procedure, and the compiler has no idea what other compilation unit you are linking to satisfy the external call. You could well be casting that pointer-to-long to something totally crazy. Or not even looking at it.
I was associated with a compiler group experimenting whole-program optimization -- doing optimization after a linking phase. This has huge benefits because now you can chase dataflow across procedure calls, which enables more aggressive procedure inlining, which enables constant folding across procedure calls on a per-call-site basis. You might also enable loop-jamming across procedure calls, etc. C really is an unholy mess as far as trying to implement modern optimizations. The compiler simply can't view enough of the program at once in order to propagate analysis sufficiently.
Do you know if anybody in CompSci or industry has put together a paper that has guidelines on this topic? Specifically, what elements of language design make it easier or more difficult to apply various optimization strategies. Just a whole collection of these for language designers to factor into their design to make compiler writers' job easy as possible.
Unfortunately, I do not, other than reading relevant conference proceedings, and sharing a cubical with the right person. None of this is in the Dragon Book, that much is certainly true.
Imagine a world where there are three languages of exactly-equal merit. X, Y, and Z. Y wins based on luck and network effects, and amazing things are done with it. Did Y bring us anything good? Not when X was there first. X would have worked fine to power those uses.
Now imagine Y was slightly worse than X, based on some magic objective measure. Even though it's the backbone of all these amazing things, Y has actually had a negative impact on the world. We could all have computers that are equally easy to program and use but slightly less buggy, if only that effort has focused on X instead.
So when you say there's no question that C brought a huge amount of value based on usage stats, you're wrong. It can definitely be questioned.
That's basically how I see it. I can even trace many CVE's and crashes to C's safety issues. Currently, CompSci people are building reliable and secure HW for us but existing C code is difficult to integrate. Had it been Oberon or something, it would be a lot easier as the developers don't meddle directly with bits and memory as much.
Compared to other low-level languages, C is a difficult language to optimise because of the aliasing rules and the incredibly vague/flexible machine model that allows it to be implemented for nearly any CPU and allows the programmer to break many assumptions that compiler writers would like to make.
All the undefined behaviour in the C spec enables optimisations, yes, but it would be so much better if those optimisations didn't require a lot of mental labour by both compiler writers and programmers.
Syntax that doesn't require you to type out "begin" and "end" for scoping and ":=" when you mean "=" for assignments are the first things that come to mind.
Begin and end annoys me a bit too because it's harder to write. Yet, we learned years later that code is read more than written. Now, all the advice in the world is to optimize code for reading, maintenance, and extension. Kind of like what non-C languages were doing, eh? Short-hand could still be useful as we pick up little things like that easily without need for verbose syntax.
Now, := is another situation. Let me ask you: what did = mean before you learned programming? I'm guessing, as math teaches, it meant two things were equal. So, making = be the conditional check for equality is intuitive and reinforced by years. A colon is English for something similar where you assign one thing a more specific meaning. So, := for assignment and = makes sense even if a better syntax exists for := part. W
hat makes no sense is making the intuitive operator for equality (=) into assignment then equality becoming ==. We know it didn't make sense because Thompson did it when creating B from BCPL. His reason: personal preference. (sighs) At the worst, if I were him, I'd have done == for assignment and = for equality to capitalize on intuition.
It makes much more sense if you consider it from a logic perspective. '=' is material implication (→), and '==' is material biconditional (↔) - which is logically equivalent to (a → b) ∧ (b → a)... so two equal signs (the logical conjunction is redundant so you drop it). Computer science favors logic, so I'd be pretty surprised if it was simply personal preference.
I actually never considered that. That's a good idea. Now, hopefully I remember this if I see other Thompson papers. Use of formal logic in them or in his background might explain it.
To match math usage, a plain = should be when the programmer wants to state that two things are in fact equal. In debug builds, it would serve as an assertion. In release builds, it would be an optimization hint.
Contradicted yourself there. The equals is used in conditionals with other logical operators to assert equality is true or not. That's a legit use for it.
Hmm. That's interesting. It's closer to the other commenters definition. I like how they straight up call it "assume" for accuracy. :)
Note: This discussion on an "obvious" problem shows how deep the rabbit hole can go in our field in even the simplest thing. Kind of ridiculous and fun at same time haha.
Showing programming languages of different types to laypersons can be interesting experience. You can see just how intuitive or not on average something is. Most people I've shown code to knew begin was the start of an activity with end being the end of it. They were hit and miss on braces as they couldn't be sure what they signified.
In this case, though, it would be fine to use something smaller like braces, brackets, or parentheses. The reason is the intuition learns it quickly since the understanding is half there already: the braces visually wrap the statements. Not a big deal to me like equality was.
Tagged add, optionally with overflow trapping, see https://en.wikipedia.org/wiki/SPARC
This was inspired by Smalltalk on a RISC and added explicitly for Lisp and Smalltalk. Also in support of this, trapping on unaligned loads.
Right. The idea being that the bottom two bits of words are tag bits. tadd dictactes that they have to be 0 for integers which means pointers are non-zero in their tags. Let's say the pointer tag is 1. That means to load a word from an object will have to compensate, eg.
car: ld r2, [r3-1]
ret
nop
If it happens that r3 _wasn't_ an object pointer, that load above would not be word aligned and thus trap. In other words, the hardware does the tag checking for you. (As an aside, the 486 introduced the option for making unaligned access trap, but AFAIK, no one has ever used it because you'd have to fix all the alignment assumptions in library code).
Loads/stores not "naturally" aligned: 4-byte boundaries for 4-byte data, 8-byte boundaries for 8-byte data.
In the late 90s I would routinely get bus errors while running Netscape on SPARC Solaris machines, presumably due to corner-case unaligned memory accesses. x86 processors perform unaligned loads and stores, but at a slight speed penalty, and with the loss of atomicity.
C got popular because it allowed low-level code to be implemented in something higher-level than assembler and moderately portably too (machines back then where as lot more dissimilar than today). It's hard to disassociate C from Unix, the former making the latter easy to port. A symbiotic relationship.
It's important to remember C was designed for the processors of the time, whereas processors of today (RISC-V included) are arguebly primarily machines to run C. C has brought a lot of good but also a lot of bad that we are still dealing with: unchecked integer overflows, buffer under- and overflow, and more general memory corruption. No ISA since the SPARC even tries to offer support for non-C semantics.