This is pretty great stuff, I knew about the raw interop features but had no idea what API Notes offered. Quite cool.
I can't help but feel that Swift will ultimately be the "slow and steady wins the race" safe language of the future. Swift steadily working "first" on both tooling and cohabitability with existing ecosystems is a huge boon for adoption. It understands what an ABI is! If I were doing a greenfield cross platform application I think Swift would be the first thing I reach for now.
The qualms I have with Swift are mostly some of the more recent complex language features that can make Swift code much harder to understand and read, as well as the brainpower required to use Swift concurrency. That and some performance concerns, though many of those seem like they may be solvable with optimizations in LLVM.
> ... some of the more recent complex language features
This isn't recent. The approach that Swift took had this path locked in from the start, the (d)evolution towards ever more spiraling complexity was inevitable from the initial choices.
And this is not 20/20 hindsight, a lot of people, including yours truly, were saying that fron the very start. As an example, take initialization:
The swift book has 16 rules and 14 pages just on object initialization. Chris replied in the comments: "the complexity is necessary for <feature we want> and thus simplicity must give way". My reply: "the <feature you want> is incompatible with simplicity and thus must give way".
→ Swift included all of Smalltalk's keyword message syntax as a special case of a special case of the method call syntax.
---
Rob Rix:
“Swift is a crescendo of special cases stopping just short of the general; the result is complexity in the semantics, complexity in the behaviour (i.e. bugs), and complexity in use (i.e. workarounds).”
Rust understands the C ABI, and that's plenty good enough for now. It's hard to guarantee safety anyway when you're linking to what's effectively outside code (not part of the same build) because we don't really have a fully typed equivalent for raw assembly or binary output (unlike your "safe" VM's, where the bytecode always undergoes sanity checks prior to execution) - hence why the raw C ABI often suffices in a practical sense.
That is surely the target for Apple platforms, whatever happens outside is more a nice to have kind of thing.
As proven by the track record of all languages that want to be simple, created as kind of anti-trends, they always tend to evolve into complexity as their userbase grows, as it turns out other programming language didn't got complex just for fun.
Then since they were initially created as kind of anti-complexity movement, the added on features always have warts due to not wanting to break compatibility, and are only half way there.
C23 versus PL/I, ALGOL variants, Scheme R7RS (full report) vs Lisp evolution, Java 26 vs Modula-3/Eiffel, Go 1.26 versus everyone, ...
I absolutely love Swift. I find it to be such an elegant language. I've done a few macOS/iOS apps with it over the years, but have really come to love it on the server. There are a couple of areas I feel could use some improvement with respect to cross-platform support, but overall the use of frameworks like Vapor have been a breeze to work with.
More support for language interoperability like this will just enhance the cross-platform experience. The Java ecosystem is what makes it so attractive to enterprises. Swift being able to easily take advantage of open-source C/C++ libraries will help with the migration.
I believe Apple is investing in C/C++ interop so much because they realize they'll likely keep their existing low-level system+embedded code rather than port it to Swift. That's good for people who want to do the same. A swift API layer can reduce the need for C/C++ developers.
But in my experience, there are sharp cliffs whenever you get off the happy path shown in the demos. That's not a problem with code where you can design workarounds, but when you wrap highly complex (if not arcane) C API, you often can't change or omit portions of the API causing problems. So while usability may be better, apinotes might not be enough to complete the work.
If you're wrapping something, I would recommend cataloging and then verifying all the language features you need to make it work before getting too far in.
> so much because they realize they'll likely keep their existing low-level system+embedded code rather than port it to Swift
I disagree. I think it’s more that it reduces the burden to port to swift. Of course there’s some stuff you’ll never be able to port because of external factors, but reducing the burden to introduce a language is the first step in allowing more stuff to be shifted to that language transparently.
Yep. They also have a history of strong C/C++ interop with objective-c being based on C and objective-c++ (which allows compiling C++ and objective-c in one code file) also being a thing. I bet part of this is a good migration path for code (Apple and 3rd party) that uses that.
This was a great read. I've used the naive approach shown in the first example before and its always felt a bit clunky, but I wasnt aware of most of these language features. I'm definitely going to try this out next time I have to write C bindings
I don't find Swift to be an ergonomic systems language at all. I changed career paths soon after its introduction, focussing quite a bit on Clojure (and now begrudgingly Python) as I did not find value in its "safety" and much prefer ObjC's closeness to Posix and CoreFoundation libraries in the Apple ecosystem. Objective-C is bare bones and awkward indeed, but much more facile in interacting with system libraries. the typing dances required to utilize Swift in this low level context was absurd. I would probably investigate Zig first, and Rust second and even C++ long before Swift.
It's good to have options. I guess this is similar effort as Swift's java interop - created to enable internal Apple needs and a cool feature to share on socials for engagement. I don't think any of this would attract people who aren't already forced to use Swift. Generally, Apple's open source/public efforts feel more like a thing they do so they can point at this during antitrust/gatekeeper lawsuits than actual healthy foss ecosystem. (which is not a surprise of course, Apple is the opposite of foss).
I wish I'd known about adding the module.modulemap file, I found out about it some time last year while making a bunch of internal libraries compatible with Swift, it works with binary frameworks too.
I'd written considerable amounts of Objective-c bridging code before that.
I think Swift has great C interop but they made pointers too diffcult to use. Which of the following type do you have to use if your C API returns some pointer?
Your question as stated is exactly why there are so many pointer types.
Is it a pointer to raw memory or a pointer to a type? Does it have a known size? Should it be allowed to be changed?
These are all questions you have to answer in C but cannot without annotations or documentation. Languages with more expressive type systems need to map that ambiguity to something.
skrrtww|1 month ago
I can't help but feel that Swift will ultimately be the "slow and steady wins the race" safe language of the future. Swift steadily working "first" on both tooling and cohabitability with existing ecosystems is a huge boon for adoption. It understands what an ABI is! If I were doing a greenfield cross platform application I think Swift would be the first thing I reach for now.
The qualms I have with Swift are mostly some of the more recent complex language features that can make Swift code much harder to understand and read, as well as the brainpower required to use Swift concurrency. That and some performance concerns, though many of those seem like they may be solvable with optimizations in LLVM.
mpweiher|1 month ago
This isn't recent. The approach that Swift took had this path locked in from the start, the (d)evolution towards ever more spiraling complexity was inevitable from the initial choices.
And this is not 20/20 hindsight, a lot of people, including yours truly, were saying that fron the very start. As an example, take initialization:
2014:
https://blog.metaobject.com/2014/06/remove-features-for-grea...
The swift book has 16 rules and 14 pages just on object initialization. Chris replied in the comments: "the complexity is necessary for <feature we want> and thus simplicity must give way". My reply: "the <feature you want> is incompatible with simplicity and thus must give way".
2020:
called it!
https://blog.metaobject.com/2020/04/swift-initialization-swi...
---
Or the syntax:
https://blog.metaobject.com/2020/06/the-curious-case-of-swif...
→ Swift included all of Smalltalk's keyword message syntax as a special case of a special case of the method call syntax.
---
Rob Rix:
“Swift is a crescendo of special cases stopping just short of the general; the result is complexity in the semantics, complexity in the behaviour (i.e. bugs), and complexity in use (i.e. workarounds).”
https://www.quora.com/Which-features-overcomplicate-Swift-Wh...
aaronbrethorst|1 month ago
peterspath|1 month ago
zozbot234|1 month ago
Rust understands the C ABI, and that's plenty good enough for now. It's hard to guarantee safety anyway when you're linking to what's effectively outside code (not part of the same build) because we don't really have a fully typed equivalent for raw assembly or binary output (unlike your "safe" VM's, where the bytecode always undergoes sanity checks prior to execution) - hence why the raw C ABI often suffices in a practical sense.
pjmlp|1 month ago
As proven by the track record of all languages that want to be simple, created as kind of anti-trends, they always tend to evolve into complexity as their userbase grows, as it turns out other programming language didn't got complex just for fun.
Then since they were initially created as kind of anti-complexity movement, the added on features always have warts due to not wanting to break compatibility, and are only half way there.
C23 versus PL/I, ALGOL variants, Scheme R7RS (full report) vs Lisp evolution, Java 26 vs Modula-3/Eiffel, Go 1.26 versus everyone, ...
pharaohgeek|1 month ago
More support for language interoperability like this will just enhance the cross-platform experience. The Java ecosystem is what makes it so attractive to enterprises. Swift being able to easily take advantage of open-source C/C++ libraries will help with the migration.
andeee23|1 month ago
just a few weeks ago i was trying to work on a swift project in neovim and found the whole langserver experience pretty bad
and it’s way worse when working on swif ui apps, but i guess that’s more of an apple wanting you to use xcode thing.
i wish there was better tooling, i like the language, but i just switched to nim for my side project
worldsavior|1 month ago
w10-1|1 month ago
But in my experience, there are sharp cliffs whenever you get off the happy path shown in the demos. That's not a problem with code where you can design workarounds, but when you wrap highly complex (if not arcane) C API, you often can't change or omit portions of the API causing problems. So while usability may be better, apinotes might not be enough to complete the work.
If you're wrapping something, I would recommend cataloging and then verifying all the language features you need to make it work before getting too far in.
dagmx|1 month ago
I disagree. I think it’s more that it reduces the burden to port to swift. Of course there’s some stuff you’ll never be able to port because of external factors, but reducing the burden to introduce a language is the first step in allowing more stuff to be shifted to that language transparently.
nicoburns|1 month ago
gregoriol|1 month ago
handstitched|1 month ago
krzat|1 month ago
Swift Package Manager handles Swift, ObjC, C, C++ in the same project, code completion works just fine. Overall much nicer than in other ecosystems.
waffletower|1 month ago
isodev|1 month ago
secretsatan|1 month ago
I'd written considerable amounts of Objective-c bridging code before that.
woadwarrior01|1 month ago
[1]: https://github.com/pvieito/PythonKit
randomNumber7|1 month ago
UnsafeMutablePointer, UnsafePointer, UnsafeMutableBufferPointer, UnsafeBufferPointer, UnsafeMutableRawPointer, UnsafeRawPointer, UnsafeMutableRawBufferPointer, UnsafeRawBufferPointer
?? This is comical and the only reason to make it this clunky is because "unsafe is bad" and you don't want people to use it.
zffr|1 month ago
- Mutable or not
- Typed or Raw
- Single object, or Buffer
Given one kind of pointer, you can convert to any other kind of pointer, but you are responsible for knowing if it’s safe to do.
The API is not super intuitive, but I can see how it makes it more clear what you are doing in your code.
dagmx|1 month ago
Is it a pointer to raw memory or a pointer to a type? Does it have a known size? Should it be allowed to be changed?
These are all questions you have to answer in C but cannot without annotations or documentation. Languages with more expressive type systems need to map that ambiguity to something.
pyrolistical|1 month ago
The human race will go extinct with the c abi still as the defacto standard at this point.
IMO any new system programming lang needs to compete over the quality of their c abi integration.
I still haven’t found a better c abi integration than zig. It can describe c funcs with higher precision than c itself.