vopilif
vopilif

Reputation: 1211

Why does typealiasing a tuple of a generic protocol in Swift allow me to treat it as a non-generic protocol?

I'm having trouble understanding why typealiasing a tuple of a generic protocol suddenly allows me to treat it as a non-generic protocol. Because of the way Swift generics work, we expect the errors we get in Examples 1, 3, 4 and 5. But why does Example 2 work? And how is it semantically different from Example 3?

Example 1:

As expected, this won't compile:

let foo: Hashable = "a" // error: protocol 'Hashable' can only be used as a generic constraint because it has Self or associated type requirements

Because Hashable inherits the Self requirement from Equatable.

Example 2:

But if I define a tuple of Hashable, it works!

typealias CompositeHashable = (Hashable, Hashable)
let foo: CompositeHashable = (1, "a") // This works!

Now I no longer need to use Hashable as a generic constraint.

Example 2b:

I can even use CompositeHashable in a collection:

let bar: [CompositeHashable] = [(1, "a"), ("b", "a")] // This works!

Example 3:

Interestingly, if I don't typealias the tuple, it doens't work.

let foo: (Hashable, Hashable) = (1, "a") // error: protocol 'Hashable' can only be used as a generic constraint because it has Self or associated type requirements

That should have been equivalent to Example 2, right?

Example 4:

Additionally, a 1-tuple doesn't work either, with or without typealias:

typealias HashableTuple = (Hashable)
let foo: HashableTuple = ("a") // error: protocol 'Hashable' can only be used as a generic constraint because it has Self or associated type requirements

Example 5:

One more piece to this.. if I take the previous typealias CompositeHashable from Example 2, and simply move it into a struct, it now gives the same error we expected in the other cases.

struct CompositeKey {
    typealias CompositeHashable = (Hashable, Hashable) // error: protocol 'Hashable' can only be used as a generic constraint because it has Self or associated type requirements
}

Can anyone explain what's going on here?

Upvotes: 3

Views: 1267

Answers (1)

user3441734
user3441734

Reputation: 17534

typealias CompositeHashable = (Hashable, Hashable)
CompositeHashable.self

// it is OK !!!!!

let foo: CompositeHashable = (1, "a") // This works!
foo.0.dynamicType   // Int.Type
foo.1.dynamicType   // String.Type
foo.dynamicType     // (Hashable, Hashable).Type

Any.self            // protocol<>.Protocol

let bar: [CompositeHashable] = [(1, "a"), ("b", "a")]
bar.dynamicType // Array<(Hashable, Hashable)>.Type    Hashable.self // error: protocol 'Hashable' can only be used as a generic constraint because it has Self or associated type requirements

.... this one is interesting

let foo: HashableTuple = ("a")

Void is a typealias for the empty tuple type, (). If there is only one element inside the parentheses, the type is simply the type of that element. For example, the type of (Int) is Int, not (Int). As a result, you can name a tuple element only when the tuple type has two or more elements.

this is equivalent of your typealias

let (a,b) = ("a",1)
typealias Htuple = (A:Hashable,B:Hashable)
let t:Htuple = (a,b)

Upvotes: 0

Related Questions