10 years on, what would you change about Swift?

As Swift’s 10th anniversary approaches it’s tempting to look back at what Swift has become and the journey it has taken. To be honest I wouldn’t have changed much but here's my personal list of things that might have been.

First, a couple of design decisions that could have gone differently:

Generic-like protocol associated type syntax?

I sometimes wonder if the syntax for associated types for protocols could have been more generic-like. The semantics and implementation would have been the same, just the syntax for introducing the associated type name could have been something more familiar along the lines of:

protocol Incrementable<Value = Double> {
    var value: Value { get }
    func incremented() -> Value
}

instead of:

protocol Incrementable {
    associatedtype Value = Double
    var value: Value { get }
    func incremented() -> Value
}

It would still be possible to introduce this as an alternative syntax and this might make the concept more approachable to people coming from other languages.

It’s a fairly arbitrary opinion but I wish there wasn’t a Substring type. Swift's no compromise String abstraction was already a bit of a handful without adding this extra complexity. StringProtocol might have been useful if NSString conformed to it but I guess that’s all water under the bridge. There was a thread about it

I share the view that SE0007 and SE0111 were unfortunate.

That’s a pretty short list of gripes all things considered but I have a longer list of features that could have been but never were. Many might have been "nice to have".

1) Custom implicit conversions

At times Swift’s type system is a tad strict and while many of the most important automatic conversions have been hard coded into the compiler it might have been nice to have a mechanism whereby you and I could add our own implicit conversions to tune the compiler down a bit e.g. Int32 to Int, UnsafePointer to String etc. I looked at this for a while and believe it could have been done without slowing down type checking

2) Lightweight sub-typing

It occurred to me you could have a subtypealias declaration to create what are known as “new types”. There was a pitch about this but unfortunately it was subject to a certain amount of gleeful pedantry early on and didn’t progress any further. I still think it would be a useful addition to the language though I’m under no illusions how big a change it would be to what constitutes a type in the compiler.

3) Lintable force unwrap.

Though it is true the Swift compiler's purpose is to compile source not judge it, I feel it could afford to be more opinionated and allow a few opt-in linting options e.g. for forced unwrap usage.

4) Suppressible warnings.

A related topic is that of being able suppress warnings. I agree that a compiler option is probably the wrong idea as Swift source should’t require build options to compile for fear of “dialects". A better idea might have been a #supressWarning(“The warning message”) source directive you could leave on the previous line. In this way marginal code could compile cleanly but still be highlighted in the source.

5) Retroactively conforming protocol extensions.

i.e. being able write an extension on a protocol that introduces a conformance to a protocol. A deceptively simple thing to want that leads to all sorts of difficulties when you come to implement it. I looked at it for a while

6) Multi-type extensions

Using protocol extensions to give behaviours to a category of types isn’t always performant. Would it be possible to write an extension on a list of types to avoid replication of code?

extension Int8, Int16, Int32 {

The use of comma conflicts with specifying multiple clauses after a where on the extension but one could almost imagine it could be supported?

7) Single quoted character literals

I’ve banged on about this enough already.

8) Values for -D compiler option like C.

There was a pitch

9) Conditionals in collections

I’d have liked to seen this but we failed at the last hurdle.

Anyway, that’s my 2¢. What would be your priorities?

13 Likes

Enforced formatting guidelines would have been nice to forever remove such discussions going forward…

28 Likes

The rest of the generics manifesto: named generic parameters, parameterized extensions, higher kinded types (maybe generic associated types ala Rust), improved variadic generics. Some of it has been pitched already, so I'm hopeful we'll get there eventually.

12 Likes

If I had the helm, I'd stop the feature creep and have at least one or two years of stabilization.

Honestly, Swift gets more and more complicated. Contrary to wide belief, it was already harder to learn than Objective-C when it was introduced due to the sheer amount of keywords and paradigms.

These days, it looks like the steering committee is on track to rival C++. Look how many new keywords and paradigms we get every year. Many of those features do not compose well and have a lot of unresolved edge cases.

The tools are also in a devastating state. lldb still hasn't fully recovered from Swift 5.5.

I love Swift, but I'm very afraid about its future.

57 Likes

You seem fairly critical in this forum, so what is it you love? Not critiquing you, as I enjoy your critical posts and agree with a lot of it, but you don't mention the love as much. Here's your chance.

What are you afraid of? It's in great shape for cross-platform availability right now, I think the only programming language that you can build on the five major productivity OS platforms: macOS, linux, Windows, Android, and iPadOS. Of course, that may be because the iPadOS license terms block other compilers than Swift Playgrounds, but still. :wink:

It has been historically too reliant on Apple to fund development, but that may be slowly changing now, with companies like The Browser Company employing @compnerd and funding Windows support.

I see no other newer application programming language doing as well right now, the closest would be Kotlin, and that's limited to Android and a little iOS. What concerns you?

5 Likes

These days you might be able to achieve both using macros.

3 Likes

I love the open evolution process, I think it works incredibly well, and I wish more open source efforts embraced the model as currently implemented.

I love directionally where Swift is headed and what the community has picked up and ran with. I would love for Apple to pour more money into the Swift teams across the board, to add extra layers of love, polish, performance and resilience to the tools we use.

As for features.

I have pet features that would love to see, but not important enough to prototype, pitch or steer to launch. I am sharing purely in case these resonate with other folks, realizing that both the implementation and opportunity cost might not justify their existence:

  • Runtime reflection would be nice.
  • Binary artifact consumption on Windows/Linux (yolo mode is ok: “I made sure the ABI vibes correctly” works for me)
  • Resolved AST-level post-processing (like macros, but rather than operating on the parsed AST, it would operate on a resolved tree, like c# does).

Miguel

22 Likes

What information does C# expose and how do you balance this against evolution of compiler internals? Today Swift's C++ AST is too complex (deep class hierarchies with hundreds of random methods) and it would not be feasible to freeze it into a stable encoding of semantic information in its current form.

Perhaps you want something more like a query language that macros can evaluate expressions in, while the underlying semantic AST remains hidden away.

1 Like

I really like Swift’s approach to macros since its restrictiveness limits macros’ compile-time impact and the potential to create inconsistent, and thus unreadable, DSLs. Although there’s still room for improvement implementation-wise, I’m really happy with how this feature turned out (especially given that I was critical of it during initial discussions).

One thing I would change is some features getting special treatment from the compiler, such as some compile-time magic in distributed-actors initializers.

3 Likes

I personally would be ok with JSON dumps equivalent to -dump-ast that I could inspect post-build, with the full understanding that the format may change from compiler version to compiler version. The S-expression output is only kind of parsable and doesn't give me the fidelity that I want, like fully-qualified symbol names everywhere.

There's even a comment in ASTDumper.cpp acknowledging the possibility of JSON output! I just need to find the time to hack it out.

3 Likes

What information does C# expose and how do you balance this against evolution of compiler internals? Today Swift's C++ AST is too complex (deep class hierarchies with hundreds of random methods) and it would not be feasible to freeze it into a stable encoding of semantic information in its current form.

They expose a library "Microsoft.CodeAnalysis" which I believe is derived on the Microsoft C# compiler.

The original C# compiler was written in C++, and there was a conscious effort to transition to a "Compiler as a Service" model written entirely in C#.

One of the first use cases for the "Compiler as a Service" was writing extensions to Visual Studio to refactor code, spot problematic code idioms and so on, you can see how these are implemented here:

Then the API matured to the point that this same functionality was repurposed for source code generation.

The API surfaced is vast, this is the entry point to the namespace:

Swift Syntax seems to already have a good chunk of this.

Miguel

12 Likes

I would add default arguments in protocol extensions :slight_smile:

I have a lot of signatures like this

public mutating func rectangle(x: Int, y: Int, w: Int, h: Int, color: Color = .white, fill: Bool = false)

which restricts my Renderer to be a struct, even though all my methods like text circle etc could be automatically implemented with just a width, height and the pixel method, making it easy to make anything a renderer, like an Image (a 2d color array), which would be very cool.

But that's impossible without losing the nice api or implementing countless overloads for every combination of arguments.


edit:
Sorry I got confused, I meant in the protocol definition itself not the extension :slight_smile:

5 Likes
  • Remove result builders

  • Remove fileprivate

  • Fix protocol extension dispatch

  • I wish ABI stability had been delayed indefinitely so that certain breaking changes could be made.

  • The language is simply too big now. I think making Swift a suitable replacement for C++ should not have been a goal, especially now with C++ interop available.

  • I think the jury is still out on structured concurrency. It's exceedingly difficult to adopt it in existing code, and not actually safe when you can just mark things as @unchecked sendable to silence errors. I find the sheer number of keywords overwhelming and not particularly intuitive, nor Swifty.

14 Likes

A C++ style Aggregate Initialization would be nice[1]. There have also been times I would have liked a little customization control over generated memberwise initializers.[2] But these have not been big blockers for me.


  1. Aggregate initialization - cppreference.com ↩︎

  2. swift-evolution/proposals/0018-flexible-memberwise-initialization.md at main · apple/swift-evolution · GitHub ↩︎

1 Like

Sharper early focus or hard goals on compile times. What design changes could have prevented long expression type inference searches?

2 Likes

May be reading his post instead of being condescending would help?

He was pretty clear about the issues which aren’t getting fixed and got worst over time:

  • Swift gets more and more complicated. Contrary to wide belief, it was already harder to learn than Objective-C.

  • These days, it looks like the steering committee is on track to rival C++.

  • The tools are also in a devastating state. lldb still hasn't fully recovered from Swift 5.5.

What is unclear here?

6 Likes

Language complexity and feature creep is worrying indeed. I'd introduce a strict "to add a feature you must remove a feature (of comparable complexity)" policy at some point and we are long beyond that point.

7 Likes

I would keep the NS prefix on the Foundation interfaces we inherited from Obj-C.

I think if we had the ability to redesign Foundation, it would probably look very different both in the breadth of problems that it tackles and how it approaches them. There's a lot of opportunity to rethink the core design decisions behind types like URL and FileManager, but now those prime names have been taken any big redesigns will have to use something a bit more awkward. So I think we should have kept the NS names - even if it's not aesthetically appealing - and waited until we had a chance to rethink these concepts from scratch.

We did that for a handful of types, including AttributedString, Regex, and Predicate, and the Swift redesigns are so much better than their inherited Obj-C interfaces. I wish we had more of those opportunities.

I think good libraries are what makes languages stand out. The language is a tool, so long as it provides what I need to accurately express concepts and manipulate data in a natural way, it's doing what I need of it. Then the optimiser needs to make it fast.

23 Likes

I just want parameter labels for closures back :crazy_face:
It's so unreasonable to type var c: (_ param: Type) -> Void from a user's perspective.

9 Likes

It is not "condescending" to ask someone for more info.

Yet, C++, which you both admit is much more complex, is also much more successful than Swift. Clearly, complexity has not killed C++.

To be clear, I agree with much of his criticism, just not his conclusion of fear. Perhaps why he's afraid is obvious to you, but you both have not adequately explained that fear to the rest of us.

9 Likes