I read this is as “C doesn’t suck” more than “C is awesome.” (I say this as someone working on a C compiler.)
I like C and it’s great for some stuff, as the author states. But what I didn’t get from the article is that it’s great for all things. The author certainly picks on C++ a lot, although it does seem like it’s not a great fit (or at least, not necessary) for his project. And yeah, C++ is incredibly complicated. But even as someone who is not a fan of C++ – C++11 has made me indifferent toward the language instead of disdainful and I currently write C++ for a living – I have to admit that C++ is much preferred to C in lots of cases, like say when you regularly need variable sized-arrays and mappings.
I love small(er) languages and much prefer them when I can use them. But don’t fool yourself into thinking large languages are meant to kill small languages. Large languages exist mainly to manage the complexity that inevitably arises when you push a small language out of its comfort zone.
Back in their heyday, C++ and Java were viewed the same way JS is viewed today. I have zero doubt that in 10 years it’ll be entirely fashionable to talk the same way about JS. In order for the industry to buy into rewriting everything yet again, there needs to be an enemy of Progress. Thankfully, there’s always an incumbent!
Knowing C++ has been an incredible asset in my career. I have done work on LLVM, written kernel modules, used it for glue code, and fearlessly integrated languages like Java and Python with C libs. As someone who is more interested in infrastructure than app dev, I’m happy to reach for it as a method of last resort.
He is right though. C’s execution model may be conceptually simple but you may need to sweat the implementation details of it, depending on what you’re doing. This doesn’t make C bad, it just raises the bar.
Or you can just test his claim with numbers. A full, C semantics is huge compared to something like Oberon whose grammar fits on a page or two. Forth is simpler, too. Whereas, Ada and Rust are complicated as can be.
I agree that there are languages considerably smaller than C. In my view, there is a small and simple core to C that is unfortunately complicated by some gnarly details and feature creep. I’ve expressed a desire for a “better C” that does all we want from C without all the crap, and I sincerely believe we could make such a thing by taking C, stripping stuff and fixing some unfortunate design choices. The result should be the small and simple core I see in C.
When comparing the complexity of languages, I prefer to ignore syntax (focusing on that is kinda like bickering about style; yeah I have my own style too, and I generally prefer simpler syntax). I also prefer to ignore the standard library. What I would focus on is the language semantics as well as the burden they place on implementation. I would also weigh languages against the features they provide; otherwise we’re talking apples vs oranges where one language simply makes one thing impossible or you have to “invent” that thing outside the language spec. It may look simpler to only present a floating 64-bit point numeric type, but that only increases complexity when people actually need to deal with 64-bit integers and hardware registers.
That brings us to Oberon. Yes, the spec is short. I guess that’s mostly not because it has simple semantics, but because it lacks semantics. What is the range of integer types? Are they bignums, and if so, what happens you run out of memory trying to perform multiplication? Perhaps they have a fixed range. If so, what happens when you overflow? What happens if you divide by zero? And what happens when you dereference nil? No focking idea.
The “spec” is one for a toy language. That is why it is so short. How long would it grow if it were properly specified? Of course you could decide that everything the spec doesn’t cover is undefined and maybe results in program termination. That would make it impossible to write robust programs that can deal with implementation limitations in varying environments (unless you have perfect static analysis). See my point about apples vs oranges.
So the deeper question I have is: how small can you make a language with
a spec that isn’t a toy spec
not simply shifting complexity to the user
enough of the same facilities we have in C so that we can interface with the hardware as well as write robust programs in the face of limited & changing system resources
Scheme, Oberon, PostScript, Brainfuck, etc. don’t really give us any data points in that direction.
So the deeper question I have is: how small can you make a language with
a spec that isn’t a toy spec
not simply shifting complexity to the user
enough of the same facilities we have in C so that we can interface with the hardware as well as write robust programs in the face of limited & changing system resources
Scheme, Oberon, PostScript, Brainfuck, etc. don’t really give us any data points in that direction.
Good question. There are few languages with official standards (sorted by page count) that are also used in practice (well.. maybe not scheme ;>):
Scheme r7rs - 88 pages - seems to be only language without useful standard library
I know that page count is poor metric, but it looks like ~600 pages should be enough :)
Given that N1256 is 552 pages, yeah, without a doubt.. :-)
The language proper, if we cut it off starting at “future language directions” (then followed by standard library, appendices, index, etc.) is only some 170 pages. It’s not big, but I’m sure it could be made smaller.
I’ve expressed a desire for a “better C” that does all we want from C without all the crap, and I sincerely believe we could make such a thing by taking C, stripping stuff and fixing some unfortunate design choices. The result should be the small and simple core I see in C.
That might be worth you writing up with hypothetical design. I was exploring that space as part of bootstrapping for C compilers. My design idea actually started with x86 assembler trying to design a few, high-level operations that map over it which also work on RISC CPU’s. Expressions, 64-bit scalar type, 64-bit array type, variables, stack ops, heap ops, expressions, conditionals, goto, and Scheme-like macros. Everything else should be expressable in terms of the basics with the macros or compiler extensions. The common stuff gets a custom, optimized implementation to avoid macro overhead.
“ What I would focus on is the language semantics as well as the burden they place on implementation. “
Interesting you arrived at that since some others and I talking verification are convinced a language design should evolve with a formal spec for that reason. It could be as simple as Abstract, State Machines or as complex as Isabelle/HOL. The point is the feature is described precisely in terms of what it does and its interaction with other features. If one can’t describe that precisely, how the hell is a complicated program using those same features going to be easy to understand or predict? As an additional example, adding a “simple, local change” show unexpected interactions or state explosion once you run the model somehow. Maybe not so simple or local after all but it isn’t always evident if just talking in vague English about the language. I was going to prototype the concept with Oberon, too, since it’s so small and easy to understand.
“but because it lacks semantics.”
I didn’t think about that. You have a good point. Might be worth formalizing some of the details to see what happens. Might get messier as we formalize. Hmm.
“So the deeper question I have is: how small can you make a language with”
I think we have answers to some of that but they’re in pieces across projects. They haven’t been integrated into the view you’re looking for. You’ve definitely given me something to think about if I attempt a C-like design. :)
He is not claiming that memory safety in general is not an issue in C. What he is saying is that in his own projects he was able to limit or completely eliminate dynamic memory allocation:
In the 32 kloc of C code I’ve written since last August, there are only 13 calls to malloc overall, all in the sokol_gfx.h header, and 10 of those calls happen in the sokol-gfx initialization function
The entire 8-bit emulator code (chip headers, tests and examples, about 12 kloc) doesn’t have a single call to malloc or free.
That actually sounds like someone who understands that memory safety is very hard and important.
I’m not familiar with either of those languages, but any idea what the author means by this?
I’m also way more interested in Zig than I am in Rust.
What I think he’s saying is that the two “big” languages are overhyped and have gained disproportionate attention for what they offer, compared to some of the smaller projects that don’t hit HN/Lobsters headlines regularly.
Or maybe it’s a statement w.r.t. size and scope. I don’t know Swift well enough to say if it counts as big. But Rust looks like “Rubyists reinvented C++ and claim it to be a replacement for C.” I feel that people who prefer C are into things that small and simple. C++ is a behemoth. When your ideal replacement for C would also be small and simple, perhaps even more so than C itself, Rust starts to seem more and more like an oil tanker as it goes the C++ way.
I agree with your point on attention. I just wanted to say maybe we should get a bit more credit here:
“compared to some of the smaller projects that don’t hit HN/Lobsters headlines regularly.”
Maybe HN but Lobsters covers plenty oddball languages. Sometimes with good discussions, too. We had authors of them in it for a few. I’ve stayed digging them up to keep fresh ideas on the site.
So, we’re doing better here than most forums on that. :)
I read this is as “C doesn’t suck” more than “C is awesome.” (I say this as someone working on a C compiler.)
I like C and it’s great for some stuff, as the author states. But what I didn’t get from the article is that it’s great for all things. The author certainly picks on C++ a lot, although it does seem like it’s not a great fit (or at least, not necessary) for his project. And yeah, C++ is incredibly complicated. But even as someone who is not a fan of C++ – C++11 has made me indifferent toward the language instead of disdainful and I currently write C++ for a living – I have to admit that C++ is much preferred to C in lots of cases, like say when you regularly need variable sized-arrays and mappings.
I love small(er) languages and much prefer them when I can use them. But don’t fool yourself into thinking large languages are meant to kill small languages. Large languages exist mainly to manage the complexity that inevitably arises when you push a small language out of its comfort zone.
Back in their heyday, C++ and Java were viewed the same way JS is viewed today. I have zero doubt that in 10 years it’ll be entirely fashionable to talk the same way about JS. In order for the industry to buy into rewriting everything yet again, there needs to be an enemy of Progress. Thankfully, there’s always an incumbent!
Knowing C++ has been an incredible asset in my career. I have done work on LLVM, written kernel modules, used it for glue code, and fearlessly integrated languages like Java and Python with C libs. As someone who is more interested in infrastructure than app dev, I’m happy to reach for it as a method of last resort.
“there needs to be an enemy of Progress”
However many the enemies, one company pledges they’ll defend Progress for as long as it takes for everyone to embrace it.
I’m not familiar with either of those languages, but any idea what the author means by this? I thought Rust has been picking up quite a bit recently.
I understood the author to be talking about the “size” of the language, not the degree of adoption.
I’m not sure that I personally agree that C is a small language, but many do belive that.
Your involvement with rust will bias your opinion - rust team hat would be appropriate here :)
He is right though. C’s execution model may be conceptually simple but you may need to sweat the implementation details of it, depending on what you’re doing. This doesn’t make C bad, it just raises the bar.
I had that opinion before Rust, and I’m certainly not speaking on behalf of the Rust team, so in my understanding, the hat is very inappropriate.
(I’m also not making any claims about Rust’s size, in absolute terms nor relative to C)
Or you can just test his claim with numbers. A full, C semantics is huge compared to something like Oberon whose grammar fits on a page or two. Forth is simpler, too. Whereas, Ada and Rust are complicated as can be.
I agree that there are languages considerably smaller than C. In my view, there is a small and simple core to C that is unfortunately complicated by some gnarly details and feature creep. I’ve expressed a desire for a “better C” that does all we want from C without all the crap, and I sincerely believe we could make such a thing by taking C, stripping stuff and fixing some unfortunate design choices. The result should be the small and simple core I see in C.
When comparing the complexity of languages, I prefer to ignore syntax (focusing on that is kinda like bickering about style; yeah I have my own style too, and I generally prefer simpler syntax). I also prefer to ignore the standard library. What I would focus on is the language semantics as well as the burden they place on implementation. I would also weigh languages against the features they provide; otherwise we’re talking apples vs oranges where one language simply makes one thing impossible or you have to “invent” that thing outside the language spec. It may look simpler to only present a floating 64-bit point numeric type, but that only increases complexity when people actually need to deal with 64-bit integers and hardware registers.
That brings us to Oberon. Yes, the spec is short. I guess that’s mostly not because it has simple semantics, but because it lacks semantics. What is the range of integer types? Are they bignums, and if so, what happens you run out of memory trying to perform multiplication? Perhaps they have a fixed range. If so, what happens when you overflow? What happens if you divide by zero? And what happens when you dereference nil? No focking idea.
The “spec” is one for a toy language. That is why it is so short. How long would it grow if it were properly specified? Of course you could decide that everything the spec doesn’t cover is undefined and maybe results in program termination. That would make it impossible to write robust programs that can deal with implementation limitations in varying environments (unless you have perfect static analysis). See my point about apples vs oranges.
So the deeper question I have is: how small can you make a language with
Scheme, Oberon, PostScript, Brainfuck, etc. don’t really give us any data points in that direction.
Good question. There are few languages with official standards (sorted by page count) that are also used in practice (well.. maybe not scheme ;>):
I know that page count is poor metric, but it looks like ~600 pages should be enough :)
Here are the page counts for a few other programming language standards:
Given that N1256 is 552 pages, yeah, without a doubt.. :-)
The language proper, if we cut it off starting at “future language directions” (then followed by standard library, appendices, index, etc.) is only some 170 pages. It’s not big, but I’m sure it could be made smaller.
That might be worth you writing up with hypothetical design. I was exploring that space as part of bootstrapping for C compilers. My design idea actually started with x86 assembler trying to design a few, high-level operations that map over it which also work on RISC CPU’s. Expressions, 64-bit scalar type, 64-bit array type, variables, stack ops, heap ops, expressions, conditionals, goto, and Scheme-like macros. Everything else should be expressable in terms of the basics with the macros or compiler extensions. The common stuff gets a custom, optimized implementation to avoid macro overhead.
“ What I would focus on is the language semantics as well as the burden they place on implementation. “
Interesting you arrived at that since some others and I talking verification are convinced a language design should evolve with a formal spec for that reason. It could be as simple as Abstract, State Machines or as complex as Isabelle/HOL. The point is the feature is described precisely in terms of what it does and its interaction with other features. If one can’t describe that precisely, how the hell is a complicated program using those same features going to be easy to understand or predict? As an additional example, adding a “simple, local change” show unexpected interactions or state explosion once you run the model somehow. Maybe not so simple or local after all but it isn’t always evident if just talking in vague English about the language. I was going to prototype the concept with Oberon, too, since it’s so small and easy to understand.
“but because it lacks semantics.”
I didn’t think about that. You have a good point. Might be worth formalizing some of the details to see what happens. Might get messier as we formalize. Hmm.
“So the deeper question I have is: how small can you make a language with”
I think we have answers to some of that but they’re in pieces across projects. They haven’t been integrated into the view you’re looking for. You’ve definitely given me something to think about if I attempt a C-like design. :)
He also says that the issues with memory-safety in C are overrated, so take it with a grain of salt.
He is not claiming that memory safety in general is not an issue in C. What he is saying is that in his own projects he was able to limit or completely eliminate dynamic memory allocation:
That actually sounds like someone who understands that memory safety is very hard and important.
Not at all the vibe I got from it.
I’m also way more interested in Zig than I am in Rust.
What I think he’s saying is that the two “big” languages are overhyped and have gained disproportionate attention for what they offer, compared to some of the smaller projects that don’t hit HN/Lobsters headlines regularly.
Or maybe it’s a statement w.r.t. size and scope. I don’t know Swift well enough to say if it counts as big. But Rust looks like “Rubyists reinvented C++ and claim it to be a replacement for C.” I feel that people who prefer C are into things that small and simple. C++ is a behemoth. When your ideal replacement for C would also be small and simple, perhaps even more so than C itself, Rust starts to seem more and more like an oil tanker as it goes the C++ way.
I agree with your point on attention. I just wanted to say maybe we should get a bit more credit here:
“compared to some of the smaller projects that don’t hit HN/Lobsters headlines regularly.”
Maybe HN but Lobsters covers plenty oddball languages. Sometimes with good discussions, too. We had authors of them in it for a few. I’ve stayed digging them up to keep fresh ideas on the site.
So, we’re doing better here than most forums on that. :)
Sure! Lobsters is where I first learned about Zig. :-)