> The fundamental problem was Objective-C was built on top of C. C inherently has pointers. It has uninitialized variables. It has array overflows. It has all these problems that even if you have full control of your compiler and tool stack, you just can’t fix. To fix dangling pointers, you would have to fix lifetime issues, and C doesn’t have a framework to reason about that, and retrofitting that into a compatible way into the system just wouldn’t really work.
If you took away C from Objective-C, you couldn’t use C arrays on the stack, for example. And if you couldn’t do that, there’s entire classes of applications where the performance just wouldn’t be acceptable. We went around, around, around. We said the only way that this can make sense in terms of the cost of the disruption to the community is if we make it a safe programming language: not “safe” as in “you can have no bugs,” but “safe” in terms of memory safety while also providing high performance and moving the programming model forward.
Microsoft, Apple, Google have the luxury to impose moving away from C on their platforms.
On other platforms you have developers that won't touch any kind of Safe C even at gun point.
I suspect that the move to swift would never have happened without LLVM. And I wouldn’t be surprised if llvm’s cross language compatibility was specifically attractive to Apple because it let them have their cake and eat it too - write a new language but keep legacy C code whenever that’s the best option.
I don't believe LLVM provides a stable cross language ABI, and AFAIK languages just use regular FFI (via the C ABI) to communicate with one another, regardless of them being implemented on LLVM or not.
Provided the compilers generate compatible versions of LLVM IR, then cross-language calls can be done at the IR level. See github.com/Keno/Cxx.jl
IIUC Swift already uses clang to load module maps in order to expose and call Objective-C, and they plan to do something similar with C++ (no link handy but I think there is an issue or enhancement proposal about it).
Is there really such a thing? I thought the ABI was dependent on the operating system and compiler.
I agree that C is unique in it's ability to generate programs that can be interfaced with from any language. This also seems to apply to older languages like Fortran and Pascal, for some reason. Higher level languages are an ABI nightmare; seriously doubt anyone will ever dynamically link against a C++ library at runtime and call a method on a C++ object from any language that isn't C++.
2. Prior to LLVM, there were much more complete attempts at this (that were even more successful, actually!)
A lot of C's endurance is related to programmer psychology, so it would be futile to design a better C.
C++, Rust, Go, Java will continue to drain the pool of C developers until it's only used for maintenance of existing projects.
This is why it's extremely irksome to see people recommend mixing C and e.g. Python for speed, or other such Frankenstein's monsters - it's just prolonging the agony of whoever will maintain that.
Rust, Go, and Java, with their memory management and cross-platform portability, and general non-determinism may eat away at the edge of C, but can never replace it.
I could see them fully replacing C if features like garbage collection and byte-code interpretation were handled at the chip-level, so languages would not have to necessarily deal with them. Could also see it happening if a chip's out-of-order execution was exposed at the language level. That would not necessarily make C worse, but Java may be more adept at handling it.
1. C is no closer than C++, or Rust (including unsafe) and they're all surprisingly pretty far from the hardware these days... this is visible the most in multi-threading, where high-level C statements are re-ordered by the compiler and even simple C statements like an integer assignment can be split into multiple instructions. Inlining and register allocation is also quite opaque and perhaps best left to the compiler, unless one uses non-standard code to force specific behaviour.
2. Well put: Go and Java were/are eating at the edge of C. Java took serious bites in the past, nowadays they're not really competing much. Today, what might have been a small Linux C program could be a Go program, especially if the performance constraints aren't too tight. Technically speaking, Rust can replace C almost everywhere, it's just that it's a significantly more complex language.
C++ introduces virtual functions, and when you make a function call, you're no longer going to a single place in memory, you go to where the class's virtual function table tells you to go, effectively jumping to a memory pointer. This is pretty helpful, but it's an incredibly different way to think about code. A function is no longer a set of instructions in memory, it's a logical representation of functionality.
And yes, I know in C, you can set a variable to a function location, and then jump there, but it's painful to do and you have to do it explicitly, so it doesn't really create an abstraction.
I am fully convinced that if Java and .NET had a proper AOT toolchain from day 1, meaning beyond dynamic linking (NGEN) or commercial offerings from 3rd party JDKs, C's influence on FOSS world would have been much less than it achieved to be.
And we would already have achieved an UNIX like stack closer to what Inferno/Limbo was all about.
Would you mind naming a few such classes?
Why would arena allocation, or evrn good generational GC fail to deliver the performance when quick allocation / deallocation of (large) arrays is required?
Java just made a couple of wrong decisions regarding AOT and value types that are now taking years to correct course, while avoid breaking backwards compatibility.
If something like Modula-3 had been granted Java's success story, we wouldn't be talking about how GC languages don't have good support for value types.
Also I think C# structs do not have mutable fields, but I could be wrong.
Edit: it looks like C# structs are mutable, my mistake.
Maybe take some time to check the other ones?
As for value types in Common Lisp, even if one bit is lost for tagging, it doesn't make arrays, structs and primitive values, less of value types.
Unfortunately, Swift doesn't fix that AFAIK.
I’ve debugged too much java code where the programmers thought that ”thread-safe” meant ”no need to think about threading issues”.
The Obj-C community (or what's left of it) is trying to make a martyr of an error-prone, cumbersome programming language.
And unlike JNI they aren't protected by a VM wall.
I find that this assertion holds about as much water for Swift as it does for Rust, Swift is not even remotely close to C++'s in terms of complexity, and that's with building in mechanisms for performant memory safety, a concept which C++ literally just shits on to this day and for the foreseeable future.
But once you learn to use the more advanced features, your code just gets easier to write and maintain. I enjoy that I can pickup new language features in Swift one at a time, figuring out where each adds its best value, and occasionally refactoring to take advantage as part of my ongoing efforts to minimize code debt.
It’s not even close. Swift is (still) minimalist by comparison to C++.
Still not seeing the problem.
Pointers aren’t magic and aren’t hard.
Pointers, in practice, are hard. Even C-family compilers cannot safely reason about them in many (most) cases.
“As an embedded engineer I can’t agree that C “has problems” because it treats you like a big boy with your big boy pants on. :)”
Edit: Nah, that’s OK. Just downvote and scamper on. Make HN more like Reddit, that’s what this place needs!
C is really good at exploiting those, while at the same time giving programmers a feeling of power.
Isn't that why everyone loves python, solving a complex problem in a handful of easily readable lines of code?
Also... I’m a little concerned that your post has “we’re all equal” social justice vibe to it... which is silly. C is more powerful than because “safety features” aren’t there.... deplorable... LOL.
You try going a day without using something programmed in C.
No social justice vibe was meant. I was trying to say that we're all bad at dealing with the kind of errors which appear in a typical C or (perhaps to a lesser extent) C++ program.
I know that some people are able to do this more successfully than others because of skill, etc, but it's not just about certain individuals. As a whole, the C community has a really poor track record at security and safety and that has a lot to do with the language itself.
Thankfully there was already C++ for those of us that wouldn't move into such a limited language versus what something like Turbo Pascal 6.0 for MS-DOS was already capable of, feature wise.
Man... to listen to hackernews tell it... no one should use C!
I wonder how many comments describing how bad C is are from people on Linux machines?
The problem with C, versus other systems programming languages it that it its pointers, implicit conversions of array into pointers, integer operations, string and array bounds don't have any kind of validation.
On other systems languages disabling such validations is explicit.
C is like JavaScript in the browser, we have to put up with it because UNIX clones won the market of commodity OSes.
C on its own without UNIX, would have failed in the market.
On the desktop world it was already being replaced by C++, Smalltalk, VB, Delphi and 4GL before FOSS based on UNIX and C started to be widespread.
Unfortunately it is impossible to convince people on pure UNIX based OSes to use something else.
Fortunately, Apple, Microsoft, Google, Sony, Nintendo are able to set which programming languages one is allowed to use on their platforms and C is on the way out on their SDKs.
I just cannot enjoy Swift at all. When I go back to Objective-C its just such a joy to write.
And I feel like I can write in Swift nearly as fast as Objective C. Objective C was always so baroque, when I define a method or variable and declare the exact same thing in a header file? Properties syntax?
I actually switched when Swift 1.0 came out (just after writing a twenty thousand line simulation engine in Objective C). It was so lightly baked I was starting to regret my decision for a couple months until version 1.2 released, only then could I ship my app commercially. Since then every release has only gotten better, and the rough edges are entirely gone. The only thing I miss from Objective C is compile time, and now just barely given how much faster the compiles have gotten.
Not all programming is the same. I've seen a lot more code churn in frontend projects than in the backend, where the actual logic and persistence happens. That makes the speed of (re)writing code relatively more important (at least for the iOS apps I've worked on, YMMV).
But even when I had to clean up legacy code, I spent most of my debugging time on on incorrect threading (typically using UIKit or Core Data from the wrong thread), AutoLayout errors, Apple framework bugs, storyboards/NIBs out of sync with code, performance degradations, or anything else where Swift doesn't help much.
In my experience, Objective-C programmers aren't allergic to safety or brevity. We love ARC! It's just that Swift has taken away too much productivity, mostly through terrible tooling and by dividing the community, for very little gain.
> Objective C was always so baroque, when I define a method or variable and declare the exact same thing in a header file
When I helped out on a Java project, I noticed that my coworkers religiously created interfaces for every single class they wrote. They couldn't really explain it beyond it being a "best practice", but I suspect they enjoyed being able to look at a short file that only contained the interface plus JavaDoc, instead of opening the full implementation every time. They basically re-invented header files :P
I don't think people would mind headers as much if Xcode was a better IDE, and we didn't have to copy declarations around manually.
Then they should have known better.
That makes sense for unit testing, in cases where mock classes would be required.
All Java IDEs have object browsers no need for header files.
Is this a joke? This sounds like "the language is wrong because it didn't completely ape C"
"Simple" things I want from generics:
1. Carry over generic information from header files to .m files, and not force us to drop all type information by having to use "id".
2. A compiler flag to enforce that the generic type must be specified at usage.
3. Where clauses in generics: Foo <T where T : NSCopying, MyProtocol>>
4. Generics on protocols, ideally with all the other features.
I know some of these are not trivial to implement, but they are mostly new syntax and hence don't have backward compatibility issues (except #2 which is why I asked for a compiler flag).
I ask, because the only way I could see it being better is if you're taking that array—that data structure—and passing it all over the program and using it everywhere. (Note to the literal-minded: this is hyperbole.) If that's what you're doing, that's not Object-Oriented Programming.
If you're instead doing real OOP, you're designing an object that will perform a clearly circumscribed bit of work for you by responding to a set of messages. The NSArray may live inside that object—in other words, the object is composed in part by using an NSArray—but that array is hidden from the rest of the program.
Generics are no big whoop-di-do in that case, because there is no client-programmer that has to worry about them; or, to put it another way, you—the original programmer—only have to worry about that NSArray with respect to the programming of that one object. In that case, the data type the array holds is not a big deal to keep track of.
So, again—seriously—what is so great about generics? The whole point of Objective-C, originally, was that it was late-binding—which, if you ask Alan Kay, was the point of OOP in the first place. If you're worrying about types all over the place, you're doing something wrong, no?
But that brings us to your first point: namely, what is the "right" way to approach OOP.
When I bring up Alan Kay, I'm bringing up a guy who was there at the start, and who set his stamp on OOP. Granted, "OOP" has been done in different ways. You'll note, however, my use of scare quotes. Allow me to explain.
When OOP had the misfortune to become a managerial buzz, somewhere back in the 1990's, and thus became a "must-have," what happened was a lot of procedural programmers simply took their procedural programming, wrapped in in C++, compiled it with a C++ compiler, nodded to the boss and assured him that they were doing OOP—and went along on their happy way.
Likewise, when college professors—set in their ways, as is their want—were told that they had to start teaching OOP, they largely did the same thing.
The result is that we have lots and lots of people out in the world "doing OOP"—who actually aren't. They're writing procedural programs, with the trappings of classes and sub-classing and what not. But all over in their programs, they are digging into the internals of their classes—and, sometimes, you see them digging in many layers deep.
And they're "doing OOP."
As I understand him, Kay is saying that you send a message to an object, on the understanding that the object will handle the message, the sender not having to worry about the ultimate who or how. If there's another kind of OOP other than that—or other than the travesty I described above—what is it?
But, that's another question, entirely. My original question was: why are generics such a big deal in OOP? I don't believe that's been answered.
I do have times when writing Swift where I miss some bit of Obj-C, but those are rare and are becoming fewer as the language develops.
Personally though, I’ve found that even when I’m keeping a close guard on things when writing Obj-C and am sure to avoid all the obvious pitfalls, bugs slip through in the most unexpected places, so I’m quite happy to have the Swift compiler double checking everything for me.
Unfortunately, as software continues disrupting various industries, it's less and leas certain that even buggy programs written by someone who thinks that safety is overrated will remain harmless.
A major feature of Swift is that it forces you to deal with error conditions and missing values. In practice you do the right thing once and early in your code, and you can write the majority of your code with guarantees that prevent major classes of bugs. Swift is not perfect, still changing, and many complain about being forced to write safe code, but it is absolutely the right move of Apple to make for their platform.
It depends on what you are building. For an app it is not a big deal to be slower. But you will not write a kernel in Swift as you will lose performance all over the place.
Python/Javascript/Lua are used as scripting languages to create video games. But the 3D engines are written in C or C++.
Each tool has its use.
That used to be the argument against C and C++ in the 80 and 90's regarding 16 bit home computers.
Any serious developer would know that Assembly was the only proper way to write software that required real performance.
Yet a couple of decades later.
> But the 3D engines are written in C or C++.
There is a world between every single string, array and numeric operation being a possible source of memory corruption and being forced to explicitly write unsafe code.
I have been using mostly safe languages since the 80's.
They only get in the way of cowboy programming, which is a very good thing for anyone that praises quality in software development.
C would no longer be around if companies got properly sued for each CVE exploit.
I have written systems in memory safe languages. The languages that provide memory safety also incur GC pauses, which means they’re unsuitable for applications where latency matters at all. (I’ve also noticed that programs written by professionals in memory safe languages tend to have many more serious high-level bugs than those written in C/C++, but I don’t have data to back that up.)
This is incorrect, you can have memory safety without a gc. An example is Ada, and more recently Rust.
Not really. RC algorithms can lead to stop the world in cascade deletion of highly nested data structures, and even cause stack overflow due to destructors being called.
Kinda sorta cheating. ;)
No. It's the inverse. People who are very good with C continue to have vulnerabilities in their code. Why? Because C.
https://www.cvedetails.com/vulnerability-list/opmemc-1/memor...
Apparently reviewing code before merging into mainline isn't of much help, but lets keep this myth of secure C code alive.
Σ (Logic errors in safe language) < Σ(Logic errors in C + Memory Corruption in C)
The default code you write in swift is safe. You spend less time worrying about its correctness, which means you can focus on solving higher level problem and less time debugging.
When people call swift pedantic, for example, and I take time to ask for the specifics, I mostly get "not familiar to me", or "I'm used to shooting myself in the foot and unwilling to consider alternatives". So my patience has run out.
extension Collection {
subscript (safe index: Index) -> Element? {
return indices.contains(index) ? self[index] : nil
}
}
And now you have the choice: array[i] // traps for invalid indices
array[safe: i] // returns nil for invalid indicesBut what I want is a way to catch all errors that others may have introduced in code I have no control over. That includes arithmetic overflow as well.
What gracefully means depends entirely on the application. Often it means to log the error or display a meaningful error message if it's an interactive app, then maybe do other kinds of cleanup and shut the thread down.
It can also mean to retry later. Array out of bounds errors are quite often just a result of not properly handling an earlier temporary error.
Whether or not errors leave the rest of the application in an inconsistent state depends entirely on the design of the application.
I'm not saying that letting an app crash on buggy code is never the right thing to do. I just want the option to handle it differently where appropriate and possible (it's not always possible anyway).
https://gist.github.com/lattner/31ed37682ef1576b16bca1432ea9...
Speaking of reliable systems, introducing an actor model is a good opportunity and excuse to introduce a mechanism for handling and partially recovering from runtime failures (like failed force-unwrap operations, out-of-bounds array accesses, etc). We explore several options that are possible to implement and make a recommendation that we think will be a good for UI and server applications.
This looks like a more principled approach that we will eventually get in Swift.
One of my goals now is to outline safe ways of using C++ for various projects and I'm not too optimistic. In fact I'm unsure what we'll end up with.
Those of us that come from Wirth languages always put quality and delivering what the program is expected to do before going crazy with optimizations.
I always got the feel that many in the C communities write code as if they were micro-optimizing every single line, just because.
And those that care about type safe driven programming in C++ usually have background in safer languages.
Perhaps because it's not a huge problem in iOS apps, but for long running multi-threaded applications this is a problem.
1. Don’t check for it at runtime and the runtime writes to memory that it shouldn’t like C.
2. Throw an exception and prevent memory corruption.
3. Don’t ever work with indexes and just use iterators.
Is there a fourth way that it can be handled in other languages?
After working exclusively in it for a while now I can't help but cringe at the practices which I see encouraged in most other languages.
Also, if Swift is so much better and is supposed to be safer to use why are apps much slower and buggier than I ever remember? My iOS devices went from instant in iPhone 1.0 to pathetic and almost not useable at times on an iPhone 6 Plus, which would still be an awesome phone for my uses (using it as an iPod essentially and the occasional call) except it's slow and buggy. Even answering the phone is no longer instant. I could have sworn I remember when Jobs introduced the iPhone he was bragging how instant everything was. What would he say now and why are apps much less stable if Swift is supposedly that much of a benefit and greater than Objective-C
I think the idea is to make common things easy and uncommon things possible.
For speed of UI response, loading times, cursor and input latency we're at best no further forward. Quite often we're in an objectively worse situation. With a tiny few exceptions, no one cares about code or data sizes any more and that is reflected in what's delivered.
That's quite apart from the rise of web apps and the slowness that necessarily introduces.
Plus, low-level languages like C are sometimes the only way to go if you are writing for embedded systems with slow CPUs.
Mikroelektronika has Basic and Pascal compilers, in addition to C, for all those slow CPUs that are market relevant.
https://www.mikroe.com/compilers
And they aren't the only ones still alive.
Firefox is on a course to remove C++ from safety critical areas and replace those with Rust.
Edit: to be clear this is based on the blog posts I’ve read from Mozilla about the introduction of Rust in different areas of Firefox. I know nothing of firm plans.
> Moving forward, the goal of Oxidation is to make it easier and more pleasant to use Rust in Firefox, and correspondingly to increase the amount of Rust code in Firefox.
And then there is the Rust page on Mozilla’s site:
> Mozilla also utilizes Rust in many of its core initiatives including Servo and key parts of Firefox.
https://research.mozilla.org/rust/
Where “key parts” is a link to the project quantum announcement: https://medium.com/mozilla-tech/a-quantum-leap-for-the-web-a...
> Initially, Quantum will share a couple of components with Servo, but as the projects evolve we will experiment with adopting even more.
”One of the best and most annoying things about Objective-C is that it has C in it. This has been hugely important for Objective-C in practice, because if you run into a performance problem with objc_msgSend, you can always rewrite that algorithm in C. That’s really, really, really important for Objective-C being successful in both in the days of NeXT on 16 MHz processors and also today for the low-level code that people are writing.”
This is indeed a great thing about Objective-C: it’s an improved C with a tight set of runtime features that help build interactive applications.
There’s a need for such a language, and I’m sad that so much of the rhetoric from Apple and the Swift community is working on the assumption that Obj-C should be completely replaced by Swift.
Swift is not a compact language like Objective-C. It’s more like C++ in how it tries to incorporate every programming paradigm that can possibly fit. The heavy focus on complex upfront typing is not necessarily the best choice for iterative app development.
Swift is useful in many places, but personally I found Objective-C’s “Smalltalk-flavored C” to hit a sweet spot for my GUI needs where integrating with C and C++ libraries is usually required all over the place.