Live data from Hacker News

50 years of C, the good, the bad and the ugly [video]

streaming.media.ccc.de

201–210 of 257 posts

Re: 50 years of C, the good, the bad and the ugly [video]

#201
I was in college early '90s in CS&E (Comp Sci & Engg) and we were taught Pascal first, COBOL and then C. Pascal and COBOL in the second year and C in the third year. C++ was already there, and gaining traction and I taught myself C++ (through the Annotated C++ Reference by Stroustrup). By the time I graduated most jobs were expecting C/C++ coding skills and my first job involved coding in C++.

Nothing compares to the raw power and control we get with C.

Around '97-98 Java was all the rage. Then Bill Gates did his internet pivot and we had .NET arriving in the '00s. The allure of being able to program in any framework language and interoperate looked like a good thing.

Over the last 20+ years I had been in the .NET world and just this year I went back to writing in C and it all came back. It is quite refreshing to be completely responsible for every aspect of your code's working. While it is sometimes tedious, nothing beats the power and control of working close to the machine abstraction.

Just my 2cents.

Re: 50 years of C, the good, the bad and the ugly [video]

#202

Earlier quoted context omitted.

What task can you do in C that you can't in Rust (or Zig, for that matter)?

No, I'd count Zig as having more of an "open ended" type system / philosophy. Though, it looks like Zig doesn't do function overloading either [1]. That's a disappointment. So you end up with `array_count`, `map_count`, etc instead of just `count`. In my way of thinking that's more work reduces readability. It's one of the paint points of C vs C++ to need `array_list_count` and `hash_map_add` instead of just saying `…

> The biggest ones for me in Rust is that it disallows extending traits for types you don't own, and the lack of function overloading. Neither of those are required for the borrow checker or safety, but it's a philosophical design decision.

The orphan rules are definitely necessary for coherence: otherwise, you could end up with a situation where two different crates try to implement the same trait for the same type, and there would have to be some (likely unwieldy) mechanism to resolve that.

Also, it's not that you can't implement any traits for types you don't own, it's that you can't implement traits you don't own for types you don't own. So you can still, e.g., create your own extension trait and implement it for whatever type you want. (But you can't do that while also creating a blanket impl for types implementing the original trait, which is a bit of a pain.) And, of course, if you need an object to implement a trait you don't own, you can define a newtype wrapper over it, but that can also be difficult to work with sometimes.

Perhaps this situation could be improved by one of the "crate-local impl" proposals that have been floating around. I'm not entirely sure how those would interact with existing implementation from the defining crates.

Re: 50 years of C, the good, the bad and the ugly [video]

#203

Earlier quoted context omitted.

Anyone discouraging a particular tool without actual context of the problem being solved gets zero respect from me. It's a massive red flag that they don't know enough to be useful. C is great for the things C is great for, however small that range may or may not be now and in the future. Any other stance is reductive and misleading.

C is a great language to program a PDP-11 in the 1980s. And that's it. There are SO MANY alternatives for so many different use cases nowadays, it's just hard to justify using C anywhere whatsoever.

It was practical for self-hosted PDP-11 development, which was tough. They could have cross-compiled a few other languages on a bigger machine, if only they had one.

Re: 50 years of C, the good, the bad and the ugly [video]

#204

Earlier quoted context omitted.

No, I'd count Zig as having more of an "open ended" type system / philosophy. Though, it looks like Zig doesn't do function overloading either [1]. That's a disappointment. So you end up with `array_count`, `map_count`, etc instead of just `count`. In my way of thinking that's more work reduces readability. It's one of the paint points of C vs C++ to need `array_list_count` and `hash_map_add` instead of just saying `…

> The biggest ones for me in Rust is that it disallows extending traits for types you don't own, and the lack of function overloading. Neither of those are required for the borrow checker or safety, but it's a philosophical design decision. The orphan rules are definitely necessary for coherence: otherwise, you could end up with a situation where two different crates try to implement the same trait for the same type,…

> The orphan rules are definitely necessary for coherence: ... and there would have to be some (likely unwieldy) mechanism to resolve that.

I think Julia, D, Nim, and others show its possible and generally easy to work with open ended type systems. I think Haskell does as well?

Though yes those come at the cost of possible conflict or user confusion, which is why I consider it a philosophical decision. It matches with the decision to not allow user code to use trait specializations in stable despite the stdlib having it.

Imports generally seem fine for controlling what gets used. Want a trait impl, import it into a module. Cargo crate features might also be a route to enforce package level decisions.

> And, of course, if you need an object to implement a trait you don't own, you can define a newtype wrapper over it, but that can also be difficult to work with sometimes.

Unfortunately that means you can't define 'default' or 'clone' traits for a type. That prevents you from using derives on your newtypes as well. That means manually implementing clone, or serde which is a PITA.

> Perhaps this situation could be improved by one of the "crate-local impl" proposals that have been floating around.

At least that'd make it somewhat easier to work with. It'd still not let end users / programmers to mix and match types and traits from different libraries without a lot of unnecessary work.

Re: 50 years of C, the good, the bad and the ugly [video]

#205
post #164

Earlier quoted context omitted.

From Dennis himself, "The language is also widely used as an intermediate representation (essentially, as a portable assembly language) for a wide variety of compilers, both for direct descendents like C++, and independent languages like Modula 3 [Nelson 91] and Eiffel [Meyer 88]. " -- https://www.bell-labs.com/usr/dmr/www/chist.html

That same document claims, "C's approach to strings works well" which, I mean I admire Dennis Ritchie but he's just wrong.

Moving goalposts, you were asking who said it, the language authors did.

If they are right or not, it is another matter.

Re: 50 years of C, the good, the bad and the ugly [video]

#206

Earlier quoted context omitted.

There are plenty of interesting C works out there, as well as data structures or algorithms that C can express elegantly. Small example, linked lists. I don't think non-C linked list code tends to be as straightforward as I've seen in C. Or the character-at-a-time style of string processing. It's kind of unique to C. You can say there is stuff about that you don't like. That's fine. Linked lists suck with modern CPU…

> as straightforward as I've seen in C To be fair, the most straightforward definition of the linked list is generic, and C completely lacks such facility. > fascist - Sounds familiar. https://news.ycombinator.com/item?id=33478321

Generic facilities can be opinionated. There are various ways to skin the cat, with different tradeoffs.

Template mechanisms cause code bloat and encapsulation violations: having to reveal the entire implementation to the clients so they can instantiate it for whatever type they want.

Some generic mechanisms have dumb restrictions, like you can make a "list of integer", whose elements, sadly, all have to be integers.

In C you can envision what you want genericity to look like at the detailed bit-and-byte memory level, and how you'd like to be able to work with it at the language level, pick some compromises where the two are at odds, and make it happen.

It is false to say that C completely lacks any generic facility because it has union types. Using a union we can put several types into a structure along with a type field which indicates which. We can overlay an integer, character, floating-point number, or pointer to something outside of the node.

Re: 50 years of C, the good, the bad and the ugly [video]

#207

Earlier quoted context omitted.

You really don't understand, but that's okay. C is a wonderful language, and that's the end of it really. You just try to think C as a language that SHOULD HAVE all the stupid bells and whistles you find convenient. Many of us more advanced coders have found that those stupid bells and whistles are, in fact, inconvenient. Casting to void is a powerful technique and the limits are literally endless. C is semantically…

The argument is not about what can and can’t be done in C. C is Turing complete and low level. You can do anything in C. The debate is not even about if it’s good idea (I don’t think it is but that’s separate). The question is about C semantics. Given the reply I get it’s pretty obvious that some here don’t understand what language semantics are. It’s about the amount of concepts you can express in the language. Hask…

The first thing you need to learn about semantics is that it's a singular: "what semantics is", not "what semantics are".

The nice thing about C is that if you're opinionated about what you want your ideal language to look like, you can use C in that pursuit. You can write your run-time in C and parts of the implementation. C is good for interfacing with operating system platforms.

You can fully bootstrap yourself off C entirely, or stick with C for the run-time and perhaps other parts. If your language isn't very good at expressing low-level manipulations, you can keep C in there as an option for writing those kinds of modules.

Some languages have used C as a target language for compilation.

C can be an enabler in the pursuit of other languages.

Re: 50 years of C, the good, the bad and the ugly [video]

#208

Earlier quoted context omitted.

Of course, you can do it that way as well. However the problem with promise approach is that it is introducing extra dynamic memory allocations beneath and these cannot be optimized out or elided. At least, not to my knowledge. With expression templates and operator overloading you're basically avoiding exactly that as much as possible.

Where do you see dynamic memory allocations being needed in that example? I only see locals that a good compiler can fairly easily optimize away. Also, there’s a simple bijection between expressions with operators and two-argument function calls: a + b * c +(a, *(b,c)) plus(a, times(b,c)) Because of that, I don’t understand why operator overloading should give better optimization opportunities than function calls.

Unless you're talking about some theoretical promise, C++ promise implementation imposes dynamic allocation.

Re: 50 years of C, the good, the bad and the ugly [video]

#209
post #61

Earlier quoted context omitted.

It’s mostly semantically very poor to be honest. C really is a nicer assembly in a lot of way. It’s extremely limited. You have branching logic, functions call, pointer arithmetic, a way to define data structures which are really memory layouts and that’s pretty much it. I guess you can appreciate that as an aesthetic statement but what you wrote would apply equally to any language with more complex semantics.

> that’s pretty much it. Only if you ignore 1. variadic arguments as in printf, 2. function pointers which are a very elementary (type-unsafe) form of closures, 3. a nice way of casting to void 4. setjmp and longjmp goodies (?) which allow you to code up co-routine libraries and exception handling mechanisms I'm sure there are more such facets I am missing. The elegance of the specification may be questionable, but t…

[deleted]

Re: 50 years of C, the good, the bad and the ugly [video]

#210

Earlier quoted context omitted.

Also without operator overloading, it wouldn't be possible to implement lazy evaluation, which along with the use of expression templates happens to be one of the most crucial aspects that any linear algebra library will want to take advantage of in order to generate the most optimal code.

Operator overloading is syntax; lazy evaluation is semantics.

That is true but a little bit unfair comment because I expanded with the context in the very next sentence. Or perhaps you're aware of alternative way of how to implement lazy evaluation without expression templates and operator overloading with all of its advantages?
Post reply on HN