Breaking changes in Swift 6

SE-0246: Generic Math(s) Functions wasn't implemented in master until now because some minor source breaking changes or some improvements of the compiler would have been necessary. Allowing minor source breaking chances could be used to fix minor (but now annoying) mistakes that were made when defining the Swift math types and functions.

It would be ambiguous if you had different labels on otherwise identically-named closures. That wouldn't be a breaking change, though, since it is currently impossible to encounter that scenario.

I think it is worth considering that not all breaking changes are created equal: if IDEs like Xcode can trivially migrate all code (by adding explicit _ labels to closure arguments, for instance), it isn't quite as big an issue. I don't mean to downplay the seriousness of such changes, of course.

rename reserveCapacity(_:) to reserve(capacity:)

2 Likes

The bar for source-breaking changes would still be active harm. We're not going to go around bikeshedding APIs with every major version of Swift.

3 Likes

Can you elaborate a bit? Is it more in the direction of

or more of a "changes that affect dozens of lines in small code bases where we can't provide automatic migration"?

I for one would be very glad to see an approach similar to Rusts editions, holding breaking changes for new language versions, but not disallowing them outright, even if the apis in question don't necessarily cause active harm.

I'd rather migrate my projects every few years, rather than have a language full of mediocre-in-retrospect design decisions and features designed in suboptimal ways just to preserve compatibility.

Assuming tooling for migrating is decent and the process as automated as can be, I can't really see a good reason to not have some (source, not abi) breakage every now and then, even keeping in mind the big kerfuffle the big break (was that 3->4?) spurred up. You'd also have a lot of time for migration, with the compiler supporting multiple language versions. Perhaps we could even allow users to migrate a file at a time instead of whole modules.

23 Likes

It’d also be nice to have facilities to change the names w/o affecting ABIs, given that guideline does evolve over time, or just decisions that doesn’t age well:

@abiname(foo(_: i, b: s))
func bar(i: Int, s: String) { ... }
6 Likes

If there was the opportunity to break anything in Swift 6 I would (personally) be looking at solving Default Protocol Implementation Inheritance Behaviour - The current situation and what/if anything should be done about it - #23 by dlbuckley

This is something that has quite a number of bug reports attached to it as when it happens results in very unexpected behaviour. There are further details about this weirdness in the thread.

7 Likes

This is a commendable attitude, but as a language we also have responsibilities to people who don't agree and who just want their existing code that was working perfectly well to continue to work while they work on other things.

11 Likes

I’m sorry if I come off as aggressive, but I have to express what I think about backward compatibility.

Python thought so as well and that compatibility attitude ended up, for a very long time, creating a wide separation between “I can’t be bothered to maintain my own code base” camp of Python 2 and “damn it, why is this library still not updated” camp of Python 3, which ended up being incredibly frustrating to everyone. Meanwhile, other people thought “this won’t happen if we don’t release a major breaking version and keep backward compatibility forever” and now, because of that attitude, C++ is such an infuriating mess, that I found myself spending 80% of my productive time either implementing safety glue code or implementing tired old design patterns (since the language will never evolve to help with those) all because some ancient unmaintained code base may be broken, becase a lot of people mistakenly think that there can exist such a thing as “code that doesn’t need to be maintained”.

Swift, historically provided two very powerful tools to better cope with breaking changes: migration tools and community attitude. And now, binary stability and module stability have arrived to provide another huge bonus in coping with breaking change.

If someone is determined to abandon their code base and hope that it will still be useful, they can configure their codebase to be built with Swift 5.2 rules and continue to write in Swift 5.2 if they plan to never migrate, and their code could be used from more modern Swift code. I think, it’s very reasonable to go by the rule “if you don’t migrate, you don’t get the new features, but you hands aren’t tied either”.

I really hope that Swift won’t fall into this compatibility trap and stay as sharp and elegant and efficient and powerful in a couple of decades as it is today!

38 Likes

+9999!

I had the same idea when I thought of a possible way of easing the migration!

5 Likes

Removing inout-to-pointer conversions would be great. It would break a lot of code, but it’d also probably fix a lot of bugs.

As someone who writes code against a lot of C API's, this would be a disaster for me.

1 Like

+9999 as well

8 Likes

I think it’s important to stress John’s point. The community on the forums (me included) are very enthusiastic about Swift and would love to see it improve and have its warts removed, even if it means breaking source compatibility. But I don’t think we represent the Swift community at large: many more programmers that work on and maintain large Swift codebases have already had to deal with the fairly chaotic changes in the previous versions of Swift and might just want the language to stabilise and mature. And those people don’t necessarily come to the forums to voice their opinion. We should be mindful of them instead.

11 Likes

One of the things I’ve always liked about Swift is that so much of what’s normally thought of as “the language” is defined in its standard library instead of in its compiler. What would we need to effectively support multiple versions/editions/whatevers of the standard library? Assuming the answer isn’t too onerous, would it be a good idea to start taking advantage of that flexibility?

1 Like

Hmmm
 I thought part of Apple’s THINK DIFFERENT campaign was/is about forward compatibility rather than backward compatibility. Apple is always thinking about the future while the competitions are still stuck with their past. It’s one thing that I like from Apple products and I would also expect it from Swift too. If the change is necessary to get a better future, then do it. Sometimes we need to break a thing to fix some things.

16 Likes

Even if that were technically possible, Apple has never taken advantage of the ability of frameworks to contain multiple version that already exists. Instead they add runtime version checks against the linked framework version to try and replicate the older behavior on newer versions. There are many reasons for this, but it boils down to most of the same arguments they used to get rid of 32-bit compatibility: memory use, maintainability, etc.

Would this potentially create a Swift version of the Python 2/3 split? I think it goes against the "one true Swift" stance.

Hmm... Creating a split is kinda the point, but I was thinking of something closer to the preview package except for “changes that we’d make if it weren’t for source compatibility requirements” instead of “additions that need a bit of real-world evaluation”.

If there isn’t a way to do it without being far less divisive than the Python 2.x/3 split, I don’t think it’s a good idea.

Is that actually necessary, or is it just an artifact of some other decision? If there are any public “why we don’t use our versioning system” blog posts or anything that Apple’s published, I’m not aware of it.

We're unlikely to get an official response, but I've asked before and that was the answer I was given. Whether it's authoritative or not I don't know.

1 Like