News for hackers. Stuff that matters.
Re:Let’s stop copying C (2016) by notacoward on Wednesday January 23, 2019 @08:03AM (#18977765)
As a C programmer for 20+ years, I find most "don't use C" articles banal and snobbish (even when parts of them are correct) but this one's really good. Well reasoned, well explained, not condescending at all. Kudos.

I like the fact that textual includes are at the top of the list. What an abomination! Modules have been known and clearly superior since forever, both for correctness and for compile times. Every time I see macro abuse in C, or exploding compile times in C++ (which oddly doesn't even get mentioned in this section) I cry a little for the future of my profession. Optional delimiters, default fall-through, weak typing - check, check, check. Good points, well argued. I've had to fix bugs caused by all of these. I happen to agree on type first, disagree on single return (and it's a shame Zig doesn't seem to be on the radar especially because of this), but these are certainly discussions worth having and the OP provides a good starting point.

It's definitely a bit long, but well worth it.

Re:Let’s stop copying C (2016) by linuxlizard on Wednesday January 23, 2019 @08:44AM (#18978002)
I work with a large vendor code base that is ~300k lines of C. There are 515 different #ifdef conditional symbols (thank you, grep). The code base started probably >20 years ago and has has who knows how many engineers working on it. There are 20+ years of standards' evolution in this code, across who knows how many chipsets, the vendor changing hands, etc. And yet, in spite of the maze, the code works very well!

I would love to see a new language, technology, something, take on this sort of problem. C works so well in this sort of problem because of the preprocessor's ability to strip out bits & pieces that aren't necessary at the time.

We need something that will allow us to fall into the pit of success rather than succumb to the easy solution of using #ifdef for new, optional features.

Re:Let’s stop copying C (2016) by notacoward on Wednesday January 23, 2019 @12:17PM (#18980204)
Is anything new really needed here? The problem that the kernel etc. solves with ifdefs and function pointers doesn't seem that different than what one can do with interface inheritance in just about any OO language. With smart inlining and LTO it can even be efficient. The problem is how to do those platform/feature checks at build time instead of run time, but I'd say that's the build system's problem ('make' or equivalent) not the language's. Ifdef there makes a lot more sense, even for languages that use modules.
Re:Let’s stop copying C (2016) by afiori on Wednesday January 23, 2019 @03:02PM (#18982167)
Well a language could have more expressive and clean macros that can still access compiler some variables.
Re:Let’s stop copying C (2016) by Sean1708 on Wednesday January 23, 2019 @11:55AM (#18979945)
> I would love to see a new language, technology, something, take on this sort of problem.

I know for a fact that Rust can do this[0], though I couldn't speculate as to whether it would be better or worse than the equivalent done with the CPP (I am fairly convinced it would be harder to fuck up at least), and I would be very surprised if the other modern systemsy languages (D, zig, etc.) didn't offer similar things.

Re:Let’s stop copying C (2016) by MrBuddyCasino on Wednesday January 23, 2019 @09:29AM (#18978349)
I suppose you mean something besides dead code elimination? Because that looks like it could be solved with proper encapsulation and abstraction mechanisms in the language, which C lacks.
Re:Let’s stop copying C (2016) by linuxlizard on Wednesday January 23, 2019 @10:27AM (#18978929)
It's a pretty amazing problem--a wifi chipset. The driver runs on Linux and Windows, also supports 3x different CPU architectures (x86, mips, arm) and multiple bus interfaces (SoC internal, usb, pci). Supports disabling optional features so 2.4Ghz only chipsets have a smaller memory footprint. Can build with/without different security options. And that's just starting in on the huge number IEEE 802.11 standards across the last 20 years. Some chips support some of the features, some don't -- but the driver still has to work on all of them.

Linux kernel is an amazing example of how to do ^^^that and still produce beautiful code. The Linux kernel uses a great number of function pointers to accomplish their encapsulation goal.

Re:Let’s stop copying C (2016) by MrBuddyCasino on Wednesday January 23, 2019 @11:18AM (#18979543)
I see where you're coming from. The description already gave me nightmares.
Re:Let’s stop copying C (2016) by Someone on Wednesday January 23, 2019 @01:26PM (#18981001)
Dead code elimination would require all code paths to be compilable by the compiler being run.

That need not be the case, and typically isn’t. If you do

   #if USE_FOO
     #include <foo.h>
   #endif
the foo.h header only need to be present on platforms where USE_FOO is defined, and need only be compilable on those platforms (it might use nonstandard compiler extensions, for example containing assembly instructions)
Re:Let’s stop copying C (2016) by pjc50 on Wednesday January 23, 2019 @08:47AM (#18978030)
> We need something that will allow us to fall into the pit of success

This is a tremendous phrase.

Re:Let’s stop copying C (2016) by golergka on Wednesday January 23, 2019 @08:54AM (#18978069)
Re:Let’s stop copying C (2016) by linuxlizard on Wednesday January 23, 2019 @10:20AM (#18978840)
Yes, I definitely learned it from a Spolsky & Atwood podcast. Not my own term (I'm nowhere near that clever).
Re:Let’s stop copying C (2016) by zeveb on Wednesday January 23, 2019 @11:31AM (#18979684)
This is something Common Lisp does with its reader macros. There's a global variable FEATURES, which is populated by one's implementation (and so might indicate OS, implementation &c.). When one reads in a file of code, there're two reader macros #+ & #- which conditionally include code, based on whether or no symbols are present in FEATURES. E.g.:

    (defvar *foo* #+sbcl(sb-sys:thing) #-sbcl(portable-thing))
The elided code isn't even truly read in, so it's not an error to refer to the package SB-SYS in a non-SBCL implementation.

I wouldn't claim that this is the end-all, be-all of code, but it does make for some reasonably compact platform-dependent-but-portable code.

Re:Let’s stop copying C (2016) by dsego on Wednesday January 23, 2019 @09:06AM (#18978169)
Have you seen ziglang.org?
Re:Let’s stop copying C (2016) by pjc50 on Wednesday January 23, 2019 @08:41AM (#18977979)
Yes, it's a good summary of points, although a few are very much a personal preference ({} delimiters).

What's not discussed is the layer below the language that so often ties us to it anyway: linkage. C also does not require anything that can be called a "runtime", although you usually get some in libc. These factors result in it being easier to write libraries in C that can be imported by FFI into more modern languages than the other way round.

Re:Let’s stop copying C (2016) by notacoward on Wednesday January 23, 2019 @08:52AM (#18978060)
Very good point about linkage and libraries. The fact that very few of the newer languages (except e.g. Rust and Zig) can be used to write universally usable libraries is unfortunate. The fact that some languages (e.g. Go) have trouble even consuming such libraries is downright antisocial.
Re:Let’s stop copying C (2016) by romeisendcoming on Thursday January 24, 2019 @05:10PM (#18993555)
Google and Mozilla at the forefront of C opposition. Instead we get walled gardens and unicorn solutions: every genius for himself.
Re:Let’s stop copying C (2016) by skocznymroczny on Wednesday January 23, 2019 @08:41AM (#18977972)
D has a "Better C" mode which removes the runtime and some of the language features. It kind of feels like C with modules, without macro abuse, without fallthrough, strong typing.
Re:Let’s stop copying C (2016) by jorangreef on Wednesday January 23, 2019 @08:31AM (#18977909)
I'm excited about Zig. It strikes the perfect balance on many points.

"I happen to agree on type first, disagree on single return (and it's a shame Zig doesn't seem to be on the radar especially because of this)"

Can you clarify what you mean by this?

Re:Let’s stop copying C (2016) by notacoward on Wednesday January 23, 2019 @09:18AM (#18978260)
Multiple return has two distinct use cases - to return multiple actual values, or to return an error status along with an actual value. Many languages can handle both cases by making it easy to create and return a list/tuple/whatever, and that satisfies both cases well enough for me. Others provide Optional or Error types which satisfy only the second case but do so in an even more concise way (no packing and unpacking of those lists/tuples/whatever). Zig is in that category, with some additional twists like errdefer and how null is handled. I'm not 100% sure I'd make the choices they did, but I think those sections of the Zig documentation are well worth reading to spur more thoughts about alternatives to C-style single return or exceptions.

https://ziglang.org/documentation/master/#Errors https://ziglang.org/documentation/master/#Optionals

Re:Let’s stop copying C (2016) by romeisendcoming on Thursday January 24, 2019 @05:14PM (#18993582)
Return status and data is a great argument for multiple value returns but it's simple enough to do in C. You just need an ADT and interface. Who hasn't done OO in C with mixed results?
Re:Let’s stop copying C (2016) by notacoward on Friday January 25, 2019 @08:36AM (#18997727)
> You just need an ADT and interface.

Yes, you can do that, but it's always going to be far more cumbersome than native multi-value return would be. Even in the simplest case (stack allocation in the caller), you have to define a new struct that has no other purpose, in a place visible to both caller and callee. You also have to refer to the status and value as struct members rather than simple variables. That's already worse than using a return value for the status and a reference parameter for the value.

I guess you could say that multi-value return is just syntactic sugar for more reference parameters. That's mostly true at the source level, but at the object level not quite. Using a reference parameter requires an extra stack operation (or register allocation) to push the parameter's address, in addition to whatever was done to allocate the parameter itself. Then the callee has to access it via pointer dereferences. With true multi-value return neither is necessary. The exact details are left as an easy exercise for any reader who knows how function calls work at that level. (BTW yes, interprocedural optimization can make that overhead go away, but a lot of code is written in C specifically to avoid relying on super-smart compilers and runtimes.)

Multi-value return isn't a huge deal, though. I appreciate having it in Python, but in almost thirty years I rarely missed it in C. While I wouldn't necessarily oppose its addition, I think it's more in keeping with C's general spirit of simplicity to leave it out.

Re:Let’s stop copying C (2016) by romeisendcoming on Monday January 28, 2019 @03:38PM (#19020852)
Who says the struct has no other value? An ADT is the only place this approach makes any sense and these are not typically comprised of two values/references alone.

A interface devised just to unwind information from a two member struct is beyond 'cumbersome'.

Re:Let’s stop copying C (2016) by ikfmpwdsoz on Wednesday January 23, 2019 @08:45AM (#18978010)
I tried to love Zig but the lack of destructors are killing me! They're such a useful tool for resource management, and apparently they weren't added in order to make all control flow "explicit" or something.
Re:Let’s stop copying C (2016) by notacoward on Wednesday January 23, 2019 @09:07AM (#18978178)
I've never actually used Zig (yet), but I think their choice is reasonable. It's not an object oriented language. Using destructors for resource management in such languages is great, but I've also seen a lot of C++ code that abuses the object-lifetime machinery. The most common is lock pseudo-objects, which "exist" only so that they can be released via a destructor when they go out of scope. Those aren't real objects. They don't contain any data. They're abstractions, which should be dealt with with other guard/context language structures. I'll admit that "defer" is a bit low-level, but it does handle most resource-release use cases in a way that's consistent with the rest of Zig.

What non-object-oriented approach would you suggest instead?

Re:Let’s stop copying C (2016) by jcelerier on Wednesday January 23, 2019 @12:29PM (#18980326)
> Using destructors for resource management in such languages is great, but I've also seen a lot of C++ code that abuses the object-lifetime machinery.

That's not abuse, that's the most important pattern in C++ (RAII). You mustn't think of C++ "objects" as Java or C# or Smalltalk objects - they are not, they are deterministic resource managers before everything.

Re:Let’s stop copying C (2016) by notacoward on Wednesday January 23, 2019 @01:11PM (#18980805)
> You mustn't think of C++ "objects" as Java or C# or Smalltalk objects

As long as people persist in calling them objects, and use all of the other object-oriented concepts/terminology such as classes and inheritance, people will expect them to be objects. That's not unreasonable. It's absurd to look down your nose at people who are taking you at your word.

These other uses are hacks. If you want to be able to attach something to a scope that's great, actually it's a wonderful idea, but just be honest about it. Make scopes a first-class concept, give things names that reflect their scope-oriented meaning and usage. Python's "with" is a step in the right direction; even though the implementation uses objects, they're objects that implement a specific interface (not just creation/destruction) and their usage is distinct. That separation allows scope-based semantics to evolve independently of object-lifetime semantics, which are already muddled by things like lambdas, futures, and coroutines. Tying them together might be an important pattern in C++, but it's also a mistake. Not the first, not the last. Making mistakes mandatory has always been the C++ way.

Re:Let’s stop copying C (2016) by edflsafoiewq on Wednesday January 23, 2019 @01:52PM (#18981339)
> If you want to be able to attach something to a scope that's great, actually it's a wonderful idea, but just be honest about it.

We don't want to attach resources to scopes, we want to attach them to object lifetimes. That's why defer/with/unwind-protect are not alternatives to RAII. The lifetime of an object I pushed to a vector is not attached to any lexical scope in the program text, it is attached to the dynamic extent during which the object is alive. While a scope guard always destroys its resource at the end of a block, RAII allows the resource lifetime to be shortened, by consuming it inside the block, or prolonged, by moving it somewhere with a dynamic extent that outlives the end of the block.

Here's an example where defer solves nothing: if I ask Zig to shrink an ArrayList of strings, it drops the strings on the ground and leaks the memory because Zig has no notion of the ArrayList owning its elements. You need to loop over the strings you are about to shrink over and call their destructors, which is literally the hard part, since the actual shrink method just assigns to the length field. The lack of destructors (Zig has no generic notion of a destructor) here impedes generic code since what you do for strings is different than what you do for ints.

RAII guards are real objects, they contain real data (drop flags), and they make code safer and more generic. If you don't like RAII, show comparable solutions (which scope guards are not), don't just call it a hack and adduce philosophical notions of how OOP should work.

Re:Let’s stop copying C (2016) by notacoward on Wednesday January 23, 2019 @02:13PM (#18981578)
> We don't want to attach resources to scopes, we want to attach them to object lifetimes.

Is that the royal "we"? Because for people who aren't you, it's only true some of the time. Sure, true resource acquisition/release is tied to object lifetimes. That's almost a tautology. But that doesn't work e.g. for lock pseudo-objects, which very much are expected and meant to be associated with a scope. It just happens to work out because the object and scope lifetimes are usually the same, but it's still a semantic muddle and it does break for things like lambdas and coroutines.

> if I ask Zig to shrink an ArrayList of strings

That's a silly and irrelevant example, having more to do with ownership rules (which C++ makes a very unique mess of) more than scopes vs. objects. Any Zig code anywhere that shrinks a list of strings had better handle freeing its (now non-) members. No, defer doesn't cover that case. Yes, destructors would, but this isn't an OO language. The obvious solution (same as in C) is to define a resize_string_list function. Again, what can you suggest in a non-OO language that's better?

> The lack of RAII here impedes generic code

You really don't want to get into a discussion about C++ and generics. Trust me on that. Yes, you need to do different things for strings and ints, but there are many ways besides C++'s unique interpretation of RAII (e.g. type introspection) to handle that.

> If you don't like RAII, show comparable solution

Done. Your turn. If you want to be constructive instead of just doctrinaire, tell us what you'd do without OO to address these situations better than existing solutions.

P.S. Also, what's with all the nonsense-word accounts in this thread taking offense at things said to jcelerier and responding with exactly the same points in exactly the same tone?

Re:Let’s stop copying C (2016) by jorangreef on Wednesday January 23, 2019 @08:54AM (#18978076)
Take a look at Zig's "defer" and "errdefer":

https://ziglang.org/documentation/master/#defer

Re:Let’s stop copying C (2016) by edflsafoiewq on Wednesday January 23, 2019 @02:02PM (#18981458)
They are not comparable to RAII, see my comment above. Even the fact it has to bifurcate into defer and errdefer suggests that it lacks the generality to replace a totalizing resource management solution.
Re:Let’s stop copying C (2016) by notacoward on Wednesday January 23, 2019 @03:05PM (#18982220)
No, they're not the same as your beloved RAII, but this isn't an object-oriented language so it doesn't have destructors. For the third time, what better solution do you propose for a non-OO language?
Re:Let’s stop copying C (2016) by gmueckl on Wednesday January 23, 2019 @11:54AM (#18979934)
This seems equivalent to scope(exit)/scope(success)/scope(failure) in D. The drawback of this construct is that you need to repeat 2 or 3 lines of code each time you need safe cleanup or a commit/abort type of construct. This can become pretty repetitive pretty fast.
Re:Let’s stop copying C (2016) by Paul_Diraq on Wednesday January 23, 2019 @07:12PM (#18984663)
I wish D had copied pythons with statement (without the scope escape) and not used the with statement for destructuring.

The nicest thing: you can throw in the exit method without the runtime falling over itself.

Re:Let’s stop copying C (2016) by gmueckl on Wednesday January 23, 2019 @11:13PM (#18985909)
I totally missed that Python has this. This seems like the saner sibling of C#'s using/IDisposable construct.

You can do a kind of RAII in D when you use structs instead of classes because these are stack allocated and have a suitable lifetime.