Which (breaking) changes would you be willing to accept in a major language release? [+ Feature wishes]

I'd like to see some improvements to:

  • Type Metadata. .Type, and .Protocol changes voice already, but improvements to Mirror, or a newer API would be appreciated.
  • Tuple, Variadic, and Function parameter list, and fixed size array changes. Personally, I'd be up for a nice mental model for these types and how they compose, even if it introduces a little bit more syntax noise.
  • A string datatype which enables python like usage. In my experience I've found solving Project Euler problems much less convenient than Swift, and yet the indexing patterns are pretty much the same.
1 Like

I quite like this, it's very natural, symmetric and it can be almost introduced as a non breaking change (if to deprecate callAsFunction instead of outright removal), but indeed subscript labels rules deserve to be straightened even if that's a breaking change on its own.

I do like the general direction. It doesn't sit well in my mind that callAsFunction (and to a lesser extent subscript) looks like an everyday function but is actually tied into syntactic magic. With Python's dunder sugar (__foo__) there's at least a hint that something special is going on (especially if it's a new corner of the language I've never used or seen before).

2 Likes

people want named subscripts because until very recently, it was not possible to vend mutable views on types with storage. now that we have _move(_:) on mutable self, i can’t think of many situations where i would want to use a named subscript instead of a full-featured nested type. it helps keep the base type from becoming cluttered with views.

1 Like

Don't worry, I was commenting on the _ part when I later realized that my comment partly didn't made any sense so I removed it. ;)

1 Like

If we REALLY wanted, we could have allowed a leading dot to refer to an anonymous function self.(_:). There is no big difference between that and callAsFunction except that it's possibly easier to parse as a human. ;)

My guess is that we can still make self.[_:] possible to refer to a subscript.

While you're not wrong, there is always have been a solution for absence of things like toggle(), yet sometimes the convenient part still wins. Requiring the user to do a 'move' dance and coming up with view types to solve this problem smells a lot like unnecessary boilerplate enforcement to me.

However I personally don't want the discussion to derail any further into those specifics. :ok_hand:

3 Likes

Perhaps not many. Consider this one for example.

Maybe just me, but subscripts are nicer / more obvious / cleaner.

I'd add generic parameters to that list — actually, I'd put that on the first position...

1 Like

If we're making breaking changes, I'd love to go back to the (now abandoned) plan of moving unapplied method references from curried functions (Self) -> (Args) -> Result to regular functions (Self, Args) -> Result. Keypath support for methods is nice, but I'd still like usable unapplied mutating methods.

2 Likes

Another one is that Set is not the same same as Set<_> inside extensions with additional constraints on elements:

extension Set where Element == Int {
  func dummyFunc() -> Set<String> {
    Set(map(\.description))
    // ❌ Cannot convert Set<Int> to return type Set<String>
  }
} 

It always catches me off guard. I’d rather have to use Self or fully specify the generic parameters.

2 Likes

Removing inheritance from the language (I'm only half kidding)

9 Likes

Some interesting things brought up and I agree with a lot, disagree with a few :laughing:

  • My biggest wish to fix if we are talking about breaking changes is Default Protocol Implementation Inheritance Behaviour - The current situation and what/if anything should be done about it Its tripped a lot of people up and addressing it would smooth out a rough and very unexpected area of swift.

  • I might be one of the few people who actually like the basic Codable, but as soon as you move outside of a 'standard' workflow it fails quickly. It would be great to address some areas around this to make it much more flexible and hopefully gain some speed. Some lessons could be learnt from Rust's Serde.

  • I also really miss having named arguments in tuples. I understand the arguments as to why they were removed, but it just feels like removing useful information from the language.

4 Likes

Make enums non-exhaustive when used across module boundaries.

Packages today basically cannot use enums in their public API if there is any possibility that new cases will be added in the future, because those cases will require a (breaking) major version increment. That's really difficult, because you need everybody to explicitly add support for the new major version in unison.

Swift has non-exhaustive enums, which force switch statements to handle @unknown default: patterns and allow libraries to add new cases later. You see that in SDK libraries, but unfortunately, it is not available to source packages.

Enums are one of Swift's best and most loved features. They've been around since it was first shown to the public. Packages are used all the time, by everyone, and they are expected to use semantic versioning to distribute updates (such as bugfixes) without breaking existing code. Unfortunately the two features are effectively incompatible with each other, and this massively limits the libraries we are able to build.

5 Likes

I would feel better if we force the use of fileprivate over private when it’s actually available in the file’s scope. Cases like private extension reuses private keyword but provides fileprivate semantics instead, introducing unnecessary confusion and styling issues.

I’m personally satisfied with fileprivate (or private(file) as someone proposed), and I believe making the semantics clear at first glance is more important than eliminating keywords.

2 Likes

I agree, especially after re-reading Default Protocol Implementation Inheritance Behaviour - The current situation and what/if anything should be done about it :flushed:

Is it there for anything other that Objective-C compatibly and references? I'd like to see both of those split off.

Yes, this would be fantastic.

Related, I would love to see exhaustivity checking possible for structs. I tend to prefer structs over enums for almost everything, but it would be nice if I could tell the compiler somehow that this is an "exhaustive" struct. Perhaps using CaseIterable or something?

2 Likes

What do you mean by "exhaustive" struct? When using in a switch statement? Please show an example.

public struct ServerEnvironment: Equatable, CaseIterable  {
    public static let allCases = [.production, .qa, .dev]

    public static let production = ServerEnvironment(host: "production.example.com", name: "Production", ...)
    public static let qa = ServerEnvironment(host: "qa.example.com", name: "QA", ...)
    public static let dev = ServerEnvironment(host: "dev.example.com", name: "Development", ...)

    private init( ... ) { }
}

// elsewhere:
let environment: ServerEnvironment = ...
switch environment {
    case .production: // do something only for Production
    case .qa: // do something only for QA
    case .dev: // do something only for Development
}

The "exhaustive" bit is that I would not need a default case because I have handled every possible case as defined by the CaseIterable conformance. It is not possible for the user of ServerEnvironment to get anything except one of those three values.

3 Likes

Maybe this will be possible if we get something like @const and then the compiler could infer that from a @const allCases or something along the lines.

4 Likes

yeah the constexpr proposal(s) have been stuck for months now :pensive:

1 Like