Is Allowing Multiple Type aliases in a Single Declaration for consistency let and var declarations a good idea? (Github Issue #48100)(SR-5528)

Hey everyone,

A while back, I showed interest in picking up GitHub issue #48100 (or SR-5528), which suggests allowing multiple typealias declarations in a single statement—kind of like how let and var already work. At the time, I got busy and couldn’t continue, but I finally have some time to finish this up.

Recap

Swift currently requires each typealias to be declared separately:

typealias Tomayto = Tomahto  
typealias Potayto = Potahto  

However, Swift allows multiple let and var declarations in a single statement:

let x = 10, y = 20  
var a = "hello", b = "world"  

The idea is to make typealias consistent, so we could write:

typealias Tomayto = Tomahto, Potayto = Potahto  

What Do You Guys Think?

It seems like a small quality-of-life improvement. More importantly, it would make the language more consistent.

Can any of you help me navigate the thousand lines scattered across 15 to 20 files in lib/Parse?

Also, do you think this needs a full Swift Evolution Proposal (SEP)? I think it doesn’t, since it’s only a minor change, but I’d love to hear your thoughts!

Looking forward to your opinions, guys! :rocket:

// Style A
typealias N = UInt, Z = Int, R = Double
// Style B
typealias N = UInt
typealias Z = Int
typealias R = Double

Which one is easier to maintain? :slight_smile:

3 Likes

In my view for consistency it would be better to remove this pattern:

let x = 10, y = 20

Not going to happen now but it should have been left out of the language. If you want two definitions on a line either use two statements separated by a semicolon or a tuple:

let (x, y) = (10, 20)
11 Likes

I think removing this is already a good idea, because accessor macros don't work with multi-pattern variables.

I don't think it's important for this to be supported, but it is inconsistent that it's not. associatedtype is the same way.

protocol P {
  associatedtype A = AnyObject
  associatedtype B = Bool
  associatedtype C = Clock
}

How documentation works for this kind of thing is really strange, so I avoid it.

enum E: Int {
  /// This documentation is used for each of the cases on this line. Is this a useful feature? 🤔
  case a = 1, b, c, z = 26
}
1 Like

The inheritance clause of an associated type is a comma-separated list, so this declares a single associated type today:

associatedtype A: Encodable, Decodable

That can't be used with defaults, which match what type aliases look like.

protocol P {
  associatedtype A = Encodable & Decodable // Compiles
  associatedtype B = Encodable, Decodable // Two errors
}

What I meant was, you can't do this:

protocol P {
  associatedtype A = AnyObject, B = Bool
}

Yeah, but defaults are less frequently used than conformance requirements, and it would be strange if , had a different interpretation in the two situations.

Also, while we’re pontificating about syntax: it’s too late to change it now and it’s a very minor point anyway, but I kind of wish things evolved in the opposite direction, without the ability to declare multiple enum elements in a single case and similar trickery.

Similarly, while pattern destructuring is useful in local context, it might have been better if stored properties did not support complex patterns and instead just had to be declared one at a time with var or let:

struct Foo {
  var (x, y) = something()
}

The reason is that this generality actually introduces some inconsistency rather than eliminating it. For example, stored property initial values become default arguments in the memberwise initializer in simple cases, but not with the above, because a single expression initializes two variables.

Also, computed properties cannot have pattern bindings for obvious reasons, but this also extends to constructs which are declared like stored properties but lower to computed properties, such as property wrappers and lazy properties:

@Foo var (x, y) = … // error
3 Likes

As someone who's written quite a few macros now, places where there's a lot of variance in surface syntax for the same semantic meaning are the bane of my existence. I'm against adding this for the simple reason that it will make processing typealiases in macros harder, for very little benefit to authors of code.

(As various other people have mentioned, I'd be in favor of removing "multiple binding" for variables, and "multiple case" for enums from the language in a future major version)

2 Likes