23 ms·
C Isn't a Programming Language Anymore
- _3u10 5y agoNah it’s a programming language author just don’t like it. You don’t have to write FFI for Linux. Use your great new language to write an OS, or one of the many OSes not written in C. Let’s face the facts, your new language probably solves some pretty theoretical problems and not problems people actually have like, hey I need to layout these structs exactly so the hardware will work. Or I can parse a packet off the wire in a reasonable amount of time.
- rayiner 5y agoMy Google SoC project was writing a c++ bindings generator for Common Lisp: https://lwn.net/Articles/147676/ https://lwn.net/Articles/147676/ (In my head this was ten years ago, but apparently it‘s nearing 17. Shit I’m getting old.) C isn’t ideal, but it’s actually not so bad and it could be worse (it could be C++). Yes parsing C is non-trivial, but that’s true of every language. These days you have libclang and a dozen other decent C parsers. Back in my day we had to use a hacked up version of GCC (and we liked it). Also, C doesn’t have a standard ABI, but every real world platform defines a C ABI. And it’s pretty simple. Meanwhile trying to handle all of the cases of C++ vtables took up weeks of my life (and I ended up shipping without fully supporting multiple inheritance, which is stupid anyway). The bigger problem for writing FFIs is, in my opinion, memory management. That’s where it gets really hard to paper over for the binding user that you’re talking to C.
- canadaduane 5y agoSoC = Summer of Code, not System on Chip for anyone else who was confused at first :)
- teaearlgraycold 5y ago> Yes parsing C is non-trivial, but that’s true of every language. Have you heard of our lord and savior LISP?
- baryphonic 5y ago> Yes parsing C is non-trivial, but that’s true of every language. Not sure about this. C is ambiguous without a symbol table, and the preprocessor is necessary for populating that. Parsing against all of the correct headers (e.g. libc, kernel headers) with correct macro values is tough. Most languages' concrete syntax treat type references in an unambiguous way (e.g. separating them from values with `:`), which is just much easier.
- dleslie 5y agoThis is a strange complaint that seems to reduce to C not having a well defined ABI. Of course it doesn't. C implementations do. This isn't really any different than most other languages, but feels different because C doesn't have a blessed implementation that all other implementations must interact with. That's a strength. It means C is found on esoteric microcontrollers as well as powerful modern desktops. That wouldn't work as well as it could if the ABI were uniform on all targets and implementations. And yes, it can act as a protocol. It's the simplest way to access host ABI communication without understanding it.
- titzer 5y agoEsoteric microcontrollers don't need an ABI. ABIs are for static and dynamic linking, and in the microcontroller world, there isn't a whole lot of linking going on. It's very common to statically compile everything into one single flash image, with libraries being in source form. E.g. this is the case in the Arduino ecosystem.
- dleslie 5y agoThey still have an internal ABI used when static linking; aligning structures, laying out data sections, function call behavior, and so forth. C's flexibility/ambivalence in this regard is why it's preferred on those architectures.
- titzer 5y agoOne could consider static linking in this scenario just an optimization for incremental builds with separate compilation of source files; it's not really used to, e.g. link a library that has no source. In that sense, it's just an implementation detail that's leaked from a build system that notionally recompiles from source but has unwisely exposed the units of its caching system. Microcontroller programs are also pretty small; it's not difficult to load the whole program into memory on a workstation and compile the whole thing together. This is how Virgil (circa 2006) worked.
- 5y ago
- ChuckMcM 5y agoInteresting rant. I found it fairly humorous but I am not sure if the author meant it that way. As I read it, the author's thesis is that all programming languages have to talk to the operating system and the operating system is written in C so the operating system uses C calling conventions which leaks C's "ugliness" into the implementation or expression of their beautiful language. I kind of think of this as the "I like computers but don't really understand computation" fallacy. It is fundamental lack of understanding about the nature of computer architectures and what they can and cannot do vis-a-vis how you might express that in a programming language. One of my professors in college was fond of saying that "All programming languages are just syntactic sugar around machine code." Which is fundamentally true, and tries to capture that at the end of the day what ever your language "says" has to be expressed in machine code to actually do what it does. You will spend a lot of time in this space writing a compiler, and code generation is an art all of its own. But you can side step, a bit, by not writing a compiler, and instead writing an interpreter. The series of articles that were posted here gave a good intro, and while you still have to do the "naughty bit" where you write code in some compilable language that can pretend to be a computer of some different form, you can make everything look like your language. I always encourage people who are "learning computers" to actually write a compiler (there are some good starting points for that but online courseware from MIT and other sources can get you the lecture material too). Doing that helps broaden one's perspective of what language designers and implementers are up against with regards to pretty much every computer working the "same" way (Von Neumann or Harvard architecture wise)
- dzaima 5y agoIt's less that the OS is written in C, but more that literally every single interface to literally anything is either written in C, or isn't meant to be used by multiple languages. C is the de facto standard for interacting with anything not written in your language. Window management, I/O, graphics, everything has a C interface. There aren't even any alternatives (besides just sending a list of bytes through a pipe or something, which noone wants to do for obvious reasons).
- ChuckMcM 5y ago
- jleyank 5y agoI have an old lapel button: C combines the power of assembly language with the flexibility of assembly language. Humorous, yes, but still true - it's a high-level, platform-independent assembly language that (used well) allows for creating all sorts of things great and small. I found it the perfect back-end to a Python front end, where the known problems of C could be minimized via control being done at the interpreter level and the compute-heavy work being done in C.
- stephencanon 5y agoBut C is neither as powerful as nor as flexible as assembly language.
- titzer 5y agoIndeed! I recently hacked up a considerable amount of assembly and used a lot of tricks that would not be possible unless I used copious gotos in C and also non-standard tail-call optimization. Some tricks (like writing inline/out-of-line codepaths) quite literally are not possible in C.
- Koshkin 5y agoI would also add then that assembly language is not as powerful or flexible as VHDL. C is a high-level programming language not unlike, say, Fortran, and its power and flexibility is in expressing calculations and concepts rather than in representing the architecture of a particular processor or in how well-optimized the generated machine code is; compilers are very good at both, and, frankly, they should be expected to do a better job than a human could in a reasonable time - especially when code is still in flux or needs to be refactored.
- School-Cotton 5y agoVectorized numeric kernels still often need to be written by hand, for one example of where modern compilers just aren’t good enough to make writing assembly obsolete.
- zik 5y agoI think this falls into the category of articles where the author makes an eye-catching assertion in the title to pull you in and then totally fails to justify it other than by ranting about some personal gripe he has.
- saagarjha 5y agoShe, and the assertion is fairly well backed (snarkily, unlike your comment…)
- AdamH12113 5y agoI'm not clear on why this is a C problem specifically, when it sounds like the author is really bothered by the lack of standardization in ABIs. Why would other languages not have this problem? At the assembly level, you still need to know which parameters go in which register and how the stack is laid out in memory. Rewriting the Linux kernel in Rust wouldn't change the way that software interrupts work, so how would it give you a Rust-native system call ABI? Am I missing something?
- varajelle 5y agoIt's not a problem with C itself. But it's a problem that C, which has loosely defined ABI, is actually used as the standard ABI of the OS. And the OS is not only about system call, but about system libraries (window manager, ...), and interoperabiloty between languages
- mzl 5y agoAs noted very clearly in the article, the problem with using underspecified versions of C as the ABI is _also_ a problem for C the language, as it limits the possible evolution of C.
- jcelerier 5y agoI guess the author would also complain that English is the primary form of communication online because of all the edge cases it has
- jart 5y agoABIs are only a problem in environments that use dynamic shared objects. But the reason why that's the case isn't obvious. So when in doubt everyone just scapegoats C. I suppose the strongest language can take the punches.
- noobermin 5y agoI literally made it to the third sentence. How is using something other than C going to fix ABI? Isn't it literally the same problem? Do other languages super mangle their symbols so there's no breakages anymore? Memory safety etc is a complaint about C but worrying about ABI breaking is literally what you'll always get when you do anything other than assembly[0]. The author laments that Rust and Swift must speak to C but that isn't because of the K&R controlled cabal, albeit it might have been the initial reason. Today the reason everything must talk to C is because operating systems are written in C and expose their API and ABI in C. Write an OS in Rust or Swift (lol) and then get mass adoption and then you won't have to worry about interfacing with C anymore. They do eventually touch on that, but as long as OS'es are in C then you need C. There unfortunately is no alternative. [0] I mean technically, you have "ABI breaks" in ASM too, it's just the program goes it's merry way being zombie like until a seg fault happens or worse.
- saagarjha 5y agoPlease read the post, this is not the “ooh we need to mangle stuff and this sucks” blog post you think it is.
- fzliu 5y agoI disagree with the author. C is an amazing language but its "lack" of an ABI is, in some ways, a byproduct of the language itself. C essentially sits half a layer above assembly and is meant to give the programmer fine-grained control over the processor and memory without requiring intimate knowledge of x86/ARM. This is why pure C projects can turn into an unmanageable behemoths. This is also why the best way to use C is to use it to implement core algorithms and data structures which can then be called by higher-level languages. Numpy/Scipy did this perfectly and now their use is now ubiquitous within the Python community. Most software engineers I know who have a background in EE love C, simply because it maps very well to what a processor actually does during execution.
- nrclark 5y ago> Most software engineers I know who have a background in EE love C, simply because it maps very well to what a processor actually does during execution. I agree with this. One thing to note though: C maps onto small CPUs pretty well. It doesn't map directly onto the x86_64 execution model at all. Today's fast CPUs are very different than the abstract machine that C represents. They run all kinds of things out-of-order, do speculative execution, run instructions in parallel, engage in branch prediction, etc. If anything the C execution model is almost a like a little VM that gets mapped onto the core that's running it.
- myrrlyn 5y ago> C is an amazing language [citation needed] > the best way to use C is to use it to implement core algorithms and data structures is this a joke. you literally cannot write containers in C unless you commit to heap-allocating everything and storing it as void* > Most software engineers I know who have a background in EE love C, simply because it maps very well to what a processor actually does during execution. lol. no it absolutely does not. i have a B.S. CpE and have actually built simple processors. the C execution model has nothing to do with how silicon operates, and modern silicon in particular goes to absurd lengths to put up a façade that c programs can use to pretend they're still on a pdp-11 while the processor goes and does other things. easy example: here's a memory address. what happens when you try to read from it
- veltas 5y agoC is still a programming language, even if you are frustrated with its ecosystem/standard.
- titzer 5y ago> My problem is that C was elevated to a role of prestige and power, its reign so absolute and eternal that it has completely distorted the way we speak to each other. Rust and Swift cannot simply speak their native and comfortable tongues – they must instead wrap themselves in a grotesque simulacra of C’s skin and make their flesh undulate in the same ways it does. Lol at this. Love this eloquent style, and pretty much agree. However, C's reign is not absolute. Virgil compiles to tiny native binaries and runs in user space on three different platforms without a lick of C code, and runs on Wasm and the JVM to boot. I've invested 12+ years of my life to bootstrap it from nothing just to show it can be done and that we can at least have a completely different userspace environment. No C ABI considerations over here. It can be as romantic as just you and the kernel in Virgil land. Heh.
- 999900000999 5y agoAnyway to build a GUI app? Definitely looks like a cool project. Can you write an OS in it, for microcontrollers ?
- titzer 5y agoHmm, very interesting question. I think you could interface with X windows via IPC, but you'd have to build that IPC layer, which ultimately boils down to pipes/sockets. So that route would be doable, but probably a steep climb. Another option would be to compile to Wasm and then import GUI-related functions that are then implemented in the Wasm host environment, e.g. JavaScript. As for microcontrollers, that was in fact, Virgil I, circa 2006. :) My prototype compiler back then generated C code and then you could compile with avr-gcc. Nowadays, I don't have a C backend or native backend for microcontrollers, so it'd require a new backend and a new exe format. Or maybe again, try to compile Wasm to AVR.
- mumblemumble 5y agoBut how would you link a library written in Rust to a Virgil program? Or vice versa? That's the real problem the author is ranting about. If you've solved that, too, then I think that the author's article isn't the only Rust team member who'd like to talk to you.
- cadence- 5y agoNot sure what the point of this rant is. C is an old language. I remember switching from Motorola assembler on my Amiga to ANSI C on my new PC and complaining how high-level it was. But it was so much easier and faster to write more complicated software with it. So it became popular and operating systems were written in it. Now we have many even higher level languages that abstract a lot of the real computing that is required for CPUs to work, and people complain every time they get exposed to the low-level stuff. Well, sorry, but on the very low level computers are actually quite complicated. Not everything can be abstracted and hidden from your eyes. As CPUs become more powerful and our systems have more RAM, we can be more wasteful with how we use these resources. So more and more of the low-level stuff can be hidden. But there will always be value in programming in C in ways that take full advantage of the hardware (especially for simple, battery powered devices).
- dzaima 5y agoA lot of the comments here are saying that C not having a well-defined ABI is acceptable/good. For people writing C, yes, that may very well be a good thing. But as the article states, pretty much every single language must interface with C to be usable, and, for them, it means either calling out to clang/gcc/tcc, or going through the mess of transliterating the C ABI of each target to your own language. not fun. I work on an implementation of a high-level language (implemented in C) that probably less than 100 people have used, and a C FFI has been asked for plenty of times. You can't get around it. Would some thing other than C being the ABI be better? Who knows. But the current situation just sucks. edit: I'd like to explicitly note that I like C as a language. But it still makes for a bad ABI, because it wasn't meant to be one, and barely even works as one.
- PaulDavisThe1st 5y agoWhat you're complaining about, as has been pointed out by several people elsewhere, is that actual real world platforms have different C ABIs. This isn't C's problem, it is a problem that C exposes. The C language doesn't define system calls. It doesn't define a lot of things that were either (a) created in C and presented via a C ABI (b) created in something even less universal and presented via a C ABI. Both (a) and (b) were done by people and projects who are not the C language and do not control the C language.
- deleted 5y ago[deleted]
- redbrown 5y ago
- notacoward 5y agoA lot of the outrage against C seems to be based on the assumption that it gained its prominence either in a vacuum or in a modern context. Oh, young ones, such is not the case. C is a creation of its time as is everything else like Linux and TCP/IP and SCSI. All of their warts seem obvious in 20/22 hindsight, but they were either not obvious at the time or were outweighed by other contemporaneous needs/constraints (usually lack of compute or other power). Bashing C without acknowledging its context and place in computing history only shows an author's shallow ignorance. On a slightly different topic, few of the problems mentioned in the OP really have much to do with C. I still remember when the interfaces to the original Mac OS (the stuff in Inside Mac, before it got UNIXified) were expressed in Pascal terms. I even vaguely remember MTS interfaces expressed in 360-assembly terms. They were absolutely no better. There is an art to making ABIs and APIs and SPIs and network wire formats and on-disk formats future proof, but it has little or nothing to do with language. The problem is that a bunch of such interfaces exist out there - because they need to exist in concrete form to be useful - that weren't defined with that level of care. It's not much more than coincidence that C happened to be a dominant language in many of those times and domains. An ABI based on a more "modern" language that involved more detailed types or (even worse) garbage collection would be far far worse for anyone using any other language. C has plenty of problems, but as a notation for expressing an ABI (much like Algol is still sometimes used as a notation for algorithms) it's really not bad.
- smackeyacky 5y agoI think I would go a little further than that. There was a point in the past where machine architectures were so alien that even getting your favourite language bootstrapped on it was a major challenge. The inflection point came when "everything became a PDP-11/VAX" which at least made formerly expensive features affordable and set off a wave of innovation that required some kind of standard (especially the Motorola 68000 and the Intel 386). Now we are so much further along, everything is starting to look like Unix in one form or another. To me, it wouldn't have mattered if the operations systems had converged on NT or VMS, the underlying demand was for Unix like systems and that is where we ended up, to the point where Microsoft have embraced it with WSL and all the big, proprietary OSs of the past are dead or irrelevent. Sure, the ABI problem is real, but 40 years ago this outcome wasn't guaranteed and it's turned out to be absolutely magic in terms of the proliferation of new languages. From my perspective, everything "having to speak C" at a low level is a feature, not a bug. It might not be a perfect model, but it works and a lot of people are familiar with it. That de-facto standardization is what makes modern computing possible.
- mhh__ 5y agoThe D compiler has a full C compiler in it for parsing headers. The compiler also does the ABIs for several targets, even more if you include the LLVM and GCC backends. C is a shit old language but we have basically tamed it.
- sys_64738 5y agoIf it isn't the neither is any other programming language.
- jmrm 5y agoMost of the problems exposed here aren't C related, but OS or architecture related. An ABI where function parameters are passed over A, B, and C registers and the return value is stored in the Z register have nothing to do with any language.
- saagarjha 5y agoOk, but the way this is exposed is via C so you really have to figure out what it is going to do to use it.
- dzaima 5y agoExcept that C defines the way nearly everything everywhere passes function parameters over registers. Not C's fault, but still a thing affecting everyone that doesn't write C, as C doesn't even try to be a good ABI, because it wasn't meant to be.
- mek6800d2 5y agoWhat does this mean? C defines the mechanism(s) for passing arguments to a function? That's news to me. The parent's point was that the binary mechanisms are OS- and architecture-dependent. (And compiler-/build-dependent too; e.g., when compilers are designed to pass arguments via registers rather than on a stack.) As other commenters have tried to point out, the diversity of target platforms precludes a single, one-size-fits-all ABI -- for C or for any other language. How does a language "try to be a good ABI"? An example of such a language would help.
- dzaima 5y agoC itself doesn't, but the OS deciding how it interfaces with C quickly results in all C programs for that OS doing the same thing, and, since C is the lingua franca of programming languages, in every single program with a cross-language API. An ABI doesn't need to be a language. In fact, I'd say that makes it worse, as now you'd have a potentially conflicting goal, wanting to make it nicer for the language itself, possibly at the cost of making for a worse ABI. But one could do well with not having varying width integers (looking especially at you, "long"), having a defined structure layout, and one (primary) calling convention per architecture (it not being the absolute best if goals change would be fine, as it'd only be used to talk between different languages, and that usually doesn't happen with such a frequency that a couple nanoseconds hurt). Sure, you'd still end up with some variance, but it'd be a lot easier to work with, and it'd be pretty hard to reach the count of 176 ABIs of C. edit: ..and just not defining things that aren't actually related to ABI (or may need to change), i.e. intmax_t
- smitty1e 5y ago> My problem is that C was elevated to a role of prestige and power, its reign so absolute and eternal that it has completely distorted the way we speak to each other. I submit that this Tower of Babel truth is older than C and a reflection of the humanity that created the tool, not the tool itself. Minimize the entropy and be at peace with existential imperfection, say I.
- Koshkin 5y agoFunny you should mention entropy. C is indeed a high-entropy programming language, and one indeed should prefer a language with fewer ways of doing or expressing the same thing.
- omgmajk 5y agoImagine if your language had to speak several other languages instead of variations of C implementations to work on different systems. Now that would be even more chaos potentially.
- cryptonector 5y agoAh, the `intmax_t` problem is a fun one. Perhaps it should never have been introduced.
- gpderetta 5y agoYes, for the most part intmax_t (and similar guarantees) is considered a mistake. In C++, the next mistake is going to be the hardware_{constructive,destructive}_interference_size constants that have serious ABI implications. I think that current GCC position is that they are not part of the ABI and they are subject to change (but also controllable from the command line).
- ddddfdohvsyknn 5y agoI sympathize with the authors pain and understand what they are striving for though I also don't have clear cut solution in mind. A lot of blood sweat and tears are expelled for compatibility amongst things that for the most part are not that important or interesting, because they are a trivial data transformation. The choice of calling convention, endianness, data layout. Imagine if an engineer's design could only work if a specific thread handedness was specified for each screw. It feels like a problem that could be solved with something better, but there's a "leaky" aspect that has to be addressed. Sometimes a uintX is a number with ordinal and/or arithmetic properties and sometimes it's a bit field where every bit has a semantic meaning.
- _3u10 5y agoHow should struct foo { int baz; float goo}; be laid out? What is the problem with the C abi that is fundamentally broken? Why shouldn’t args be passed on registers where possible? What’s the problem with the ABI?
- ntauthority 5y agoThis feels exactly like why Microsoft (and others) during the 90s started to define a well-defined subset of C, with some fancy IDL stuff, and a C-like ABI where all the C stuff needed could also be generated from the IDL, as standard somewhat-object-oriented interfaces like COM and other DCE RPC-likes. Of course, the POSIX-likes never adopted this, whereas Microsoft nowadays has some fourth generation of this IDL stuff (WinMD) that they are also slowly porting all the old C API definitions to (see win32metadata, also used for defining stuff like the Win32 package for Rust). Also, of course, this all has its own issues too, for one COM's definition of reference counting is a bit picky, and there were a lot of advanced 'implicit RPC' features that were also more inherent footguns, but at least it doesn't involve what is ranted about here.. mostly.
- pavlov 5y agoAgreed. This seems like a rant from someone who perhaps never used anything but Linux and Darwin (macOS). Windows went to substantial lengths to support non-C languages. It’s not obvious that the result was much of an improvement.
- saagarjha 5y agoFrom the blog post: > Case Study: MINIDUMP_HANDLE_DATA I think the author is well aware of Windows.
- sanxiyn 5y agoGNOME developed GObject Introspection and it works well for GNOME.
- Ericson2314 5y agoIt really sucks for cross compilation.
- Too 5y agoIf you reach a bit over into IPC/RPC, rather than just linking, the options with decent IDL and cross language code generators start to increase quickly. For example D-bus, or androids binder. Some even use grpc or thrift between processes on same machine. All of these are of course operating one or two abstraction levels up but are still good to consider.
- phendrenad2 5y ago...You've just discovered why interfaces like COM and Cocoa are a good idea, yes. Welcome to 1999. Join me in cursing the proliferation of POSIX and FFIs. JOIN ME.
- Koshkin 5y agoWin32 API is another example of something that has been pretty stable… But then, it is basically C. (Just like COM is basically C++.)
- phendrenad2 5y agoCOM was meant to be language-agnostic, so I don't see how it's basically C++...
- Koshkin 5y agoInterfaces are made to match the C++ vtable. Using them from C, for example, is somewhat of a hassle. (Not to say anything about IDL…)
- ntauthority 5y agoNot exactly. They use a different calling convention (for systems where 'this' pointers are normally passed in a special place) from usual C++ classes to match C-ish, and there's a few other restrictions for things (overloads, inheritance) that otherwise differ across C++ ABIs and wouldn't map well to other non-C++ languages. The C wrappers generated by MIDL are also fairly similar to raw C++, compare (where pUnk is an IUnk *): pUnk->Something(&bA); with... IUnk_Something(pUnk, &bA); which expands to something like this: pUnk->lpVtbl->Something(pUnk, &bA);
- flohofwoe 5y agoCOM can also be seen as a mapping of C++ APIs (with some extras like interface versioning) onto C APIs, mainly as workaround for C++'s missing ABI stability (so that C++ APIs can be exposed via DLLs in a (C++) compiler-agnostic way).
- charcircuit 5y ago>hear that everything on Linux is “just a file”, so let’s open a file on Linux! That man page is for glibc. You instead want to look at man syscalls and man syscall to look up how to interface with he kernel. Linux does not require programs to use any amount of C.
- saagarjha 5y agoOk, but what of other OSes?
- BatmanAoD 5y agoOkay, here's the Linux syscalls(2) man page: https://man7.org/linux/man-pages/man2/syscalls.2.html https://man7.org/linux/man-pages/man2/syscalls.2.html The second sentence is: > System calls are generally not invoked directly, but rather via wrapper functions in glibc (or perhaps some other library). Okay, so we can't avoid C that way, but what's the "invoked directly" way? Well, it's syscall(2): https://man7.org/linux/man-pages/man2/syscall.2.html https://man7.org/linux/man-pages/man2/syscall.2.html This man page actually reiterates what the other one, that you shouldn't be making syscalls directly! But in any case, what is `syscall()`? ...it's a C macro. So yeah, Linux does require programs to use C. You've just been isolated from it.
- Mathnerd314 5y agoIt's true that the man pages assume C and the calling conventions are based on the C ABI, but it is possible to code the syscalls directly in assembly. In particular Go and Zig have such "bare-metal" syscalls.
- BatmanAoD 5y agoYou still have to follow the C ABI when interfacing with C. That's the exact problem being called out in the post. Zig solves interoperability by incorporating an entire copy of LLVM. Go does do bare metal syscalls, but as mentioned elsewhere in these comments, this has caused breakage on Mac when the kernel was updated, because this interface isn't stable.
- ratboy666 5y agoC descriptions of the OS interface API... Ok - I get that. But, there is a system call interface. int 0x80, syscall. Now, these can be wrapped, and the result exposed in a completely different way. But that is the definition. If its "C" on one side and "C" on the other... well, ok then! I though Linux had vDSOs to allow direct "ABI" calls for performance reasons.... utilizing that will force a certain "C-ish" look. In turn, that can be wrapped. None of this changes quickly. Heck. CP/M-80 had "CALL 5" with registers a certain way. Wasn't "C" by any stretch! Because the C (POSIX, mostly) "API" is stable and available, we tend to use it. Wasn't always the case -- after all, FORTRAN I/O was all the rage back in the 60s (cf SNOBOL4). If (whatever) programming system wants to avail itself of the C infrastructure, it is certainly free to do so. Stop the endless whinging about C! Why C? It is the only language in its class that works from Z80 to my Thinkpad.
- valleyer 5y agoOn some platforms (Darwin, among others), the syscall interface is defined not to be stable. Syscalls must go through Libsystem stubs -- which is written in C. Go made the mistake of assuming the Darwin syscall interface was stable, and an OS update broke binaries.
- warmwaffles 5y ago> Rust and Swift cannot simply speak their native and comfortable tongues – they must instead wrap themselves in a grotesque simulacra of C’s skin and make their flesh undulate in the same ways it does. Sure they can, you just need to build the mechanism that allows them to.
- thatsnotc 5y agoNone of this is C. All of this is the OS. Who specifies how you talk to the OS, and how native applications talk to each other? The OS does. And it does, in fact, differ across different OSes (with different calling conventions). The only reason C comes into this picture is because it runs on all the platforms these other languages do (and many more), so you can write an adapter between the language and C, and not have to worry about supporting 10 million different calling conventions, because some C compiler author has done that for you. So the article is half right; this isn't a programming language. But C sure is. (And that's not to mention the fact that to some extent the ABI and calling convention is determined more by the CPU architecture than the OS, much less the language!)
- varajelle 5y agoC is an obsolete [1] programming language that must still be used for interoperability between languages despite not being particularly good at that. [1] Yes it is. It is lacking many important features. And people claiming they rather not have these features are like an author writing a book with notepad.exe because they think modern Word processor are too complicated.
- thatsnotc 5y agoI don't think we have the same definition of "obsolete" (C is definitely not "no longer produced or used"). There is absolutely no requirement to use C and you could implement it directly in Assembly if you chose, or in many other languages (most of which pre-date C).
- Drillgon200 5y agoNotepad is absolutely not obsolete. It's far better than a heavy word processor for quickly pasting something into or jotting down a note because it's fast and has hardly any extra interface to get in the way, and has very very little bloat. This is definitely something that authors claim as well, George RR Martin writes on MS DOS because modern word processors have too many bloated features and correct his spelling.
- dlsa 5y agoSo many expletives. Perhaps a calmer and long term view might change the world. How? Take Linux. Currently written in C. Yay! Write a module for it in something else. Good. Perhaps you've heard of Rust? Ok, great. Write some more modules in that. Write even more. Even more. Now there's just a lot of Rust talking C ABI. Hmmm. If only... oh wait. Let's change that. Time passes. Boom! The whole thing is Rust. No C ABI target at all other than for C applications, which is how things should be. This is one way to proceed and it may have already begun. https://www.zdnet.com/article/rust-in-the-linux-kernel-why-it-matters-and-whats-happening-next/ https://www.zdnet.com/article/rust-in-the-linux-kernel-why-i... In reality C is a classic and has weathered many storms, fads and changes in fashion. Even with other languages in the linux kernel it will probably stick around for a lot longer.
- flohofwoe 5y agoAnd what about other languages than C and Rust? There still needs to be an "interface lingua franca" between OS internal modules too, not just between user code and "system DLLs".
- dlsa 5y agoWhat about them? As you say you still need a calling convention. I'm not disagreeing with that. And I chose rust at random because its an available systems language. Why not something else? You don't actually need C at the bottom layer. It won because the OS are most commonly written in C, it's encountered elsewhere and therefore popular. Usually because it helps get the desired result. That isn’t nothing either. In days of olde there was pascal calling convention. Why? The os was written to that, because it was written in that. Surprise! I'm not even suggesting these are bad things. Linux for me is a kernel. The choice of C is purely an implementation detail based on pragmatic decisions to suit its purpose. I am merely suggesting that the calling convention isn’t locked at all layers for all time. Yes you need a common convention. But it needn't be C and it doesn't need to be C all the way up or down either. I'll leave it there. Seems that discussing this kind of thing isn't wanted. Side note: I wonder what would have happened if I'd chosen Lua for my example. Would people have gotten hung up on the fact that its commonly implemented in C? Would they have locked onto that yet not realised that it needn't actually be written in C at all? If you wish to explore that, swap rust for lua in my previous comment. There are ways of using lua as a calling convention without even referencing C. If you want a more realistic example, swap in dart or more likely D. Everything I've said still applies. And yes you can have mutliple calling conventions or even a simpler one. Its just a convention of how to pass parameters, amongst other things. Thats basically it.
- jollybean 5y agoPeople are talking about C, I think the issue is organisational structure, capitalism, lack of governance. Universities are too academic and companies are too selfish or busy. Writing native modules for Java, Node or anything else you run into these shenanigans which are very costly to the world. I really do think in 2021 that G, MS, Nnvidia, Intel etc. should get together and create something simple and clear for ABIs and more importantly get everyone on the program. That, and Javascript 2, which dumps a lot of its weirdness and comes with a comprehensive standard lib.
- Koshkin 5y agoAny programmer can use any programming language that he wants so long as it is C. Henry Ford
- deleted 5y ago[deleted]
- redbrown 5y ago
- haberman 5y agoThis article is hard to parse because it doesn't present any alternatives that the author considers better. - You have to speak the C ABI to talk to the OS -- yes, if you want an OS to support more than one language, or you want multiple languages within a single process, you need a common ABI of some sort. What is the alternative that doesn't involve a common ABI? - C has too many ABIs -- yes, there are a lot of different kinds of hardware, and sometimes multiple software ecosystems evolved in parallel on the same hardware (eg. Windows and Linux). What is the alternative? - ABI changes are hard to make in a non-breaking way -- yes they are. It's fundamentally a hard problem. You could compile everything from source every time, but the open-source world seems to have decided that there isn't enough time or CPU power to "live at head" and build from source every time, like Google does: https://abseil.io/about/philosophy#we-recommend-that-you-choose-to-live-at-head https://abseil.io/about/philosophy#we-recommend-that-you-cho... (Disclosure: I work at Google). - "You Can’t Actually Parse A C Header" -- this is, IMO, the most actionable objection that the article raises. You could imagine a subset of C that does away with macros, typedefs, etc. in order to be easier to parse, and in doing so forms a more accessible ABI specification language. That seems eminently doable. But parsing is only part of the overall battle: actually implementing the ABI for 176 triples seems like the bigger problem. Especially when you add the ABI-impacting function attributes mentioned in the tweet. As a C fan, it's hard to see the language itself blamed for what are (in my view) just fundamentally difficult problems.
- dzaima 5y agoI don't think the author blames it on C. C didn't choose to become the ABI of 99% of the world. But yet, it is. > You have to speak the C ABI to talk to the OS [...] What is your alternative that doesn't involve a common ABI? Emphasis is on the _C_ ABI. The alternative is something that at least tries to be designed as a universal ABI. > there are a lot of different kinds of hardware, and sometimes multiple software ecosystems evolved in parallel on the same hardware If there was an explicit ABI standard though, evolving ecosystems would have to go through the standardization and thus you just couldn't get conflicting ABIs for the same thing. If such standardization could cut the number of ABIs in half, it'd still be a huge win. And I think it'd be possible to go much further than that. Furthermore, it may be possible to split orthogonal things apart, such that instead of e.g. 50 targets, you have 5 OS targets and 10 arch targets, each specifying a separate part of the ABI. (current ABIs obviously do this to some extent, but not in any specified & structured way) > As a C fan, it's hard to see the language itself blamed for what are (in my view) just fundamentally difficult problems. I also like C. I agree these are hard problems. But I don't think they're problems that C should be dealing with. But we're pretty much just stuck with C.
- deleted 5y ago[deleted]
- layer8 5y agoWhat we need is an ABI-level IDL [1] to specify library interfaces, that every programming language could use for imports and would translate to implicit declarations, both for calls to the interface and/or for implementing the interface. Currently, C header files poorly serve that purpose, and at the same time, that use impedes the evolution of C itself. Such an IDL wouldn't necessarily solve C's own compatibility issues with its evolution, but it could dramatically improve the FFI situation if widely adopted. [1] https://en.wikipedia.org/wiki/Interface_description_language https://en.wikipedia.org/wiki/Interface_description_language
- notacoward 5y agoPersonally I'm inclined to think that an IDL is the Right Way to specify an interface. OTOH, that has been tried, many times (ntauthority describes one series of attempts by MS but there have been others). Every one has succumbed to secondary complexity and/or simple lack of adoption. One common issue I've seen has been that the definitions end up feeling pretty non-idiomatic for most languages because they only support a minimal common subset of features. Would exceptions be more idiomatic? Too bad. How about union types? Too bad. What about ownership and memory lifetimes? Oops, alien again. In the end it's no easier to work with than the C definitions, plus generated code often creates problems with build systems and IDEs, so people just go with what already 99% works and deal with its warts as best they can.
- layer8 5y agoIt would need tighter integration with programming languages and compilers, e.g. you should be able to write something like `#include "libfoo.idl"` (similar to the ease with which you can write `extern "C"` in C++, or `__declspec(…)` in MS C, etc.) and not need an extra code-generation step. Regarding features, it’s clear that you’d need to find some sort of common ground, though not necessarily just the lowest common denominator.
- yeetboomers 5y agoWow. I knew that some folks really didn't want to be told that their pet language wasn't the end all be all, and I know people hold strong opinions about [insert language here], but the comments in this thread are somehow even more revealing than the usual fair of comments insisting that [insert language here] isn't used for anything despite its use in shipping (low-powered) embedded devices, end-user consumer-facing products, kernels, webservers, frontends, bootloaders, memeory allocators, etc, etc. But to go on and assert that it "has" to be this way, or that its this way strictly out of necessity... I don't know how to describe it politely without saying "stockholm syndrome". What is it about C, or about a certain subset of C-fans that results in them asserting that 'it has to be this way' or tossing derision at those who strive for better? Do some of you realize that is born out of living in an ecosystem where we don't have to deal with this bullshit and thus don't see it as "normal" the same way you do? It's okay. I've watched the exact same type of excitement faced with derision on countless technologies in the past. Oh how wrong I've been to bet on [insert cluster technology here] or [insert linux service manager here], surely those would never take off because hand-maintained bash scripts are fine right? Just like an undefined fragile ABI is fine, right? It kind of makes me think of some long screed I read last night from someone making alls sorts of claims about Linux ond the Desktop while insisting that they use Xorg. Meanwhile, under Sway/wayland, I am driving 3 monitors at different resolutions and refresh rates and they all perform perfectly on testufo.com). Your trauma doesn't make your choices better. Your supposed growth in the face of now-unnecessary pain doesn't make you smarter. I have contributed code that runs on thousands of peoples desktops every day, in both C and [insert language here] and I cannot believe this is still a fucking conversation. I mean, thankful for one side of it, but sad that in yet another facet of life there are folks insisting that the shit-we-have is the best possible. Sad.
- WalterBright 5y agoC's most infamous parsing difficulty is: A * B; Is it A multiplied by B, or B declared as a pointer to A? It can't be resolved without a symbol table. But there's another way that works very well: it's a declaration. The reason is simple. The multiply has no purpose, and so nobody would write that. C doesn't have metaprogramming, so it won't be generating such code as an edge case (unless using the preprocessor for metaprogramming, in which case you deserve what you get). But there's a worse problem: (A) - B Is it A minus B, or casting negative B to type A? There's just no way to know without a symbol table. One might think who would write code that has a vacuous set of parentheses around an identifier? It turns out they don't, but they write macros that parenthesize the arguments, and the preprocessed result has those vacuous parentheses. D resolves both issues with: 1. if it parses like a declaration, it's a declaration 2. a cast expression is preceded by the keyword `cast` and D is easy to parse without a symbol table.
- omegalulw 5y agoI don't see why resolution without a "symbol table" is a big deal. Knowing whether A is a type or not resolves these fairly easily. And in well written code, it should generally be obvious whether A is a type or not so it should not be a readability problem either.
- bodhiandphysics 5y agoIt’s annoying when parsing c. Also the situation is worse in c++ since * can have side effects!
- WalterBright 5y agoYou're right, and C++ is hopeless to parse without a symbol table in other ways. Later versions of C++ added keywords to disambiguate, but of course there's legacy code.
- WalterBright 5y agoBecause a quite different AST is built for the second case depending on whether A is a type or not. And you can't tell whether A is a type or not without a symbol table.
- kahlonel 5y agoThese C-hating articles are getting out of hand on HN. I see atleast one every week on the front page. If you don't like C, STFU, pick-up whatever FOTM BS you like, GTFO of here, and leave us "normies" alone who have to get actual stuff done which absolutely requires C.
- xmonkee 5y agoI don't think you read the article
- voidhorse 5y agoidk man, C is catching a lot of hate lately, but I honestly don’t see why. C is a fine programming language, in fact, outside of maybe the lisp, logic programming, and concatenative language families, C pretty much is the only programming language. Every other language really just wants to be C while fixing a lot of problems that aren’t actual language problems but rather programmer problems. If we were all perfect human beings C is suitable for any task and would be flawless. But programmers make mistakes and often attempt to code in languages without actually understanding the language (a good example of this is not understanding memory models) and tons of modern features do not exist if you remove C as the motivating, negative factor they were a response to. And sure, because of the way things shook out, programming languages have to speak C in order to do something useful as fast as possible, but if you carry this sort of argument to its logical ends, it amounts to the same thing as saying “I would love programming if only I didn’t have to compile to machine code” ASM has the same problems—architectures have different instruction sets. Or further, this is like complaining about the fact that you need to code against a particular fundamental design scheme like von neumann. This is just the nature of computing and different ideas and markets yielding different products. Also this article makes a mistake of criticizing C on the grounds that it’s now a protocol and poorly designed as a protocol while simultaneously admitting that it wasn’t designed to be a protocol—so it seems a bit unfair to call these “bad design decisions” on those grounds. Furthermore, it’s a lot easier to make these sweeping criticisms than to effectively solve what is not only a massive technical but also social problem.
- na85 5y ago>a lot of problems that aren’t actual language problems but rather programmer problems This is a really naive take. In aerospace design we have the concept of "Design for Human Factors" or "Human Factors Engineering" which is basically an acceptance that the pilot or crewmember is a fallible, flawed "system". That is to say in aerospace we recognize that human beings have limits. They have finite breadths of attention, and they panic in a crisis, and they make mistakes when fatigued, and they're not capable of performing repeated tasks 100% consistently. That's why for example we make the "fuel cut off" switch a different size and color than the intercom switch. That's why we make the engine fire extinguisher handle very prominent and easy to grab, etc. For how many decades have software folks been writing use-after-frees and off-by-ones? When is Software as a field going to get over its collective hubris and admit that C is poorly designed from a Human Factors standpoint? A tool that requires unattainably- or unsustainably-high human performance is a shitty tool; get a better one.
- lngnmn2 5y ago
- tayistay 5y ago> Now C isn’t just a programming language, it’s a protocol. But the image above that is calling conventions for functions on a particular processor. What does that have to do with C? > So actually semantically parsing a C header is a horrible nightmare I'm confused. It seems to me the right way to go would be to improve C such that it's more precisely specified, easier to parse, etc. Genuine question: why is that so hard? (I suspect the reasons are more institutional than technical.) Variable declarations are syntactically weird in C, for example. And newer languages seem to have better ways (that are actually context-free). So why couldn't C be evolved toward that? > int foo(int x, int y) becomes > foo(x: i32, y: i32) -> i32 and so on. > You don’t see England trying to improve itself, do you? I guess that's a saying I'm not familiar with. Seems to me England has improved a lot over the years. Go easy on me, geniuses.
- dragonwriter 5y ago> It seems to me the right way to go would be to improve C such that it's more precisely specified, easier to parse, etc. Genuine question: why is that so hard? It's not hard to specify a language that is all those things compared to C, but then it is, ipso facto, not C.
- tayistay 5y agoI don't get it. At one point C changed its syntax for function arguments (significantly, I'd say), was that new language ipso facto not C?
- School-Cotton 5y agoC compilers still support old-style function argument syntax. They have to, for backwards compatibility. Plenty of actively-developed software being compiled on modern versions of the C standard still has some older functions whose signatures are written in K&R style.
- tayistay 5y ago
- gumby 5y agoOl' Bappyscript can just invoke open() from assembly code; no need to write a little C shim. That's usually how I end up doing system calls anyway.
- varajelle 5y agoThe question is how do you call open() from assembly code? On which register do you pass which arguments? Since open() is defined in C, you basically have to speak the C ABI, and therefore C. And open was just an (easy) example. Now do that for xcb_create_window and all the other functions of all the system library you want to interact with.
- asguy 5y ago> Since open() is defined in C, you basically have to speak the C ABI, and therefore C. .. but what does open() call? Do you think that's air you're breathing? (The GP is implying you can just call directly into the kernel via a syscall,.. and all you need to worry about is your OS's syscall interface. You don't necessarily need the C ABI; unless of course your OS's kernel uses it for syscalls, at which point: sorry-not-sorry.)
- gpderetta 5y agoWell, I know that's not really the point, but for xcb_create you can just speak the X11 protocol over the wire. No need to call any xlib function.
- jasfi 5y agoI prefer to use Nim. C is the default backend and abstracts a lot of pain away.
- cryptonector 5y agoMost of this is not really C-specific, really. The ABI issues are likely to arise in any systems programming language, and in any lingua franca between languages. These things come up in C first because C is the lingua franca.
- varajelle 5y agoWebAssembly (wasm) will save us all! https://hacks.mozilla.org/2019/08/webassembly-interface-types/ https://hacks.mozilla.org/2019/08/webassembly-interface-type... Who knows, in the future maybe wasm will be the common ABI between languages.
- sally1620 5y agoIt is just the simple fact that the user land API of all current operating systems is C. A programming language need to support at least C ABI for this purpose.
- lngnmn2 5y agoOk, I have entered into a wrong shitfest. First. An ABI had nothing to do with C, it is an OS/hardware calling conventions - how the stack is used and procedure's parameters being passed. That is a machine code level. Second. Redefinition of hardware types is beyond bullshit. I can't even comment on this. Third. Yes, Every. Single. Architecture will have its own ABI. This is how linux kernel is organised (as a direct consequence of the above) and it has nothing to do with C. Again, it is a machine code level. The rest is not worth commenting. I missed the fact who wrote the article, so I am ok with being flagged in this discussion. This is the new normal.
- deleted 5y ago[deleted]
- jokoon 5y ago"there are languages people complain about, and languages nobody use" (quote from the creator of C++) It's easy to complain about C or C++, but I don't see that many languages that compiles to machine code, and offer compilers for many platforms. I agree that C is somehow the new "assembly". C is the glue used for building operating system, so it's not surprising you need to use it in many places. I'm sorry but I don't see any better language that is as simple to learn for students as C, and fixes some problems of C. Most new languages are often difficult to read, introduce a lot of un-needed sophisticated features, and are not that much used because they're too niche. A good example is Rust. Sure, the safety is an awesome feature, but ADA already did it somehow, but it's not what developers need. There is a reason the world talk english and not german or japanese. It's because english is just much, much easier to learn. Nobody cares if the language makes sense. It's odd because HTML and javascript are so much more ambiguous and cause a lot of pain, yet I hear more complaints about C or C++.
- 5560675260 5y ago> It's odd because HTML and javascript are so much more ambiguous and cause a lot of pain, yet I hear more complaints about C or C++. HTML implementations (and to extent JS) are very forgiving - you could have thousands of warnings and errors raised by linter, but page will still render mostly fine. In this regard C would be closer to XHTML which would fail to render unless it's "well formed" and, well, nobody uses it nowadays.
- deleted 5y ago[deleted]
- jstimpfle 5y agoNo offense but this analogy is plain wrong. C is definitely like HTML, not XHTML, in that respect.
- MrBlueIncognito 5y ago
- dvh 5y agoBoth C and C++ are unfixable. You have to start new language.
- gpderetta 5y agoIf I was wrangling on FFI issues across multiple platforms and battling gratuitous incompatibilities, bad documentation and bugs, I would be ranting as much or more as the author. The issue is that there isn't really an alternative. Obviously a cross platform ABI is never going to exist (different endianness, alignment, register usage, stack conventions just considering the CPU, OSs themselves add more complexity). We could have an IDL to describe in details the ABI, but then: - you need all OS vendors to be on board, which is not going to happen. - even if they did you can be sure it will be forked in a myriad of dialects and non conforming implementations. - even if everybody plays ball, bugs will still happen. The best next is for language designers to come up with community maintained IDLs and tooling to interface with various languages instead of waiting for platform vendors to provide them. This is a realistic solution that can work, but at this point you might just accept that C fulfils this role already even if it is far from ideal. Just embrace libclang and hold your nose. edit: there is also the option of targeting a single virtualized platform like the JVM or CLR which is great, but not really appropriate for a system language.
- Annatar 5y ago
- tayistay 5y agoI find the notion of “how many do we need” an insult to human creativity. Also, simply asserting that those making new languages want to “carve out a name for themselves” is highly presumptuous.
- Annatar 5y agoWhen one's person creativity causes another a lot of work and unnecessary problems, then we have a problem. And a huge problem at that. This is exactly the case.
- tayistay 5y agoHow does the existence of another programming language cause a lot of work and unnecessary problems? Like, the existence of, say, Zig, which I haven’t used, hasn’t seemed to cause anyone any problems.
- Annatar 5y agoAll it takes is for someone to "get creative" and write a mission critical piece of software in their newly invented language, and then one gets stuck deciphering that mess when things go awry, which they always do. Can you not see the consequences, or have you never had that happen to you?
- dzaima 5y agoNot just Rust, but every language will need to interface with C sooner or later. And not just to the OS, but to the window manager, graphics APIs, libraries (ffmpeg etc). So, unless you think that C is the perfect language for doing literally everything, this still is a problem. Assembly won't at all help you because the interface to anything other than syscalls (which are stable pretty much only on linux, and linux isn't the only operating system) is the C ABI for the specific OS & architecture combination. Yes, C works pretty much everywhere, but thus every language needing to support C is a mess.
- ezekiel68 5y agoA lot of smoke here, but very little light. What this article fails to capture is the fundamental question that is faced any time a new architecture is encountered: should 'int' be sized (number of bits) according to its original (or most recent) de-facto definition? Or should it be sized according to the natural register size of the architecture's general-purpose registers? A lot of us went through this back in the mid-2000's when AMD64 (x86_64) came out. It took both Microsoft and the Linux crowd (just to name two communities) time to come up with their respective (and incompatible) translaitons of types. The top answer to this StackOverflow question summarizes this well: https://stackoverflow.com/questions/384502/what-is-the-bit-size-of-long-on-64-bit-windows https://stackoverflow.com/questions/384502/what-is-the-bit-s... In the gaming industry, we came up with our own typedefs until the standards caught up. Things like int_8, int_64, etc. I say only a fool would ever use somehting as pretentious a concept as 'intmax_t' in normal code (which isn't a tranlation table of typedefs based on platform). "max" according to whom? That is not the way.
- Decabytes 5y agoI feel like there has been a lot of energy put into creating system programming languages that fix the shortcoming of C and C++. But it seems to me that the solution is to just realize that we rely on C too much in code bases, to talk to OSes, and between programming languages to ever get rid of it. So instead of trying to replace C in all of those cases, just make a better C. Simplify the language to the point where parsing it is almost trivial. Barring all of the above, why not just create a new standard where people agree that A * B; And (A) - B Can only mean one thing. Sure this wouldn’t do anything for the old code, but at least where new C code was required we wouldn’t continue dealing with all of its complexity. I feel like this is the same problem with CSVs. It is trivial to parse if people follow the standard but most people don’t. But if you create a parser that only accepts the standard and refuses to parse non standard CSVs then you never have to deal with any of the BS edge cases that come up. And if you show where in the code/CSV the ambiguity is, people might actually fix their code to get it to parse correctly instead of leaving it ambiguous
- junon 5y agoEveryone's here talking about ambiguous symbols but anecdotally speaking I've maybe run into this problem once or twice in roughly 10 years. Hardly an argument, in my opinion, and it's annoying to see parroted on every thread about C's syntax.
- xyzzy21 5y agoWell, I can tell it's a programmer and not an engineer who works at hardware levels. All the complaints are "1st world" or "high level" programmer/computer problems. If you've ever designed hardware and had to bootstrap it up to a "high level" situation - then these are nonsense concerned when you are merely trying to get a new hardware design up and running. You can always bootstrap to a language that already has all these things. The author obviously has never done anything like that. Not based in any reality where C matters.
- 1MachineElf 5y agoIn the same vein, my favorite troll on this topic is "The C language is purely functional" (2009) from Haskell programmer Conal Elliott's blog: http://conal.net/blog/posts/the-c-language-is-purely-functional#:~:text=%E2%80%9CC%20programmers%E2%80%9D%20really%20program%20not,nestable)%20expressions%2C%20not%20statements http://conal.net/blog/posts/the-c-language-is-purely-functio....