jameslintaylor
jameslintaylor

Reputation: 349

why is this causing so much trouble? (protocols and typealiases on their associated types)

I'm trying to do something along the lines of this:

protocol Protocol {
    associatedtype T
    associatedtype ArrayT = Array<T>
}

struct Struct<ProtocolType: Protocol> {

    func doSomething(with: ProtocolType.ArrayT) {

        let _ = with.map { $0 } 
        // ^ compiler complains on this line
        // "value of type ProtocolType.ArrayT has no member map"
    }
}   

where I define a convenience typealias ArrayT that uses the associatedtype T. It seems that when I try to use ArrayT like in doSomething(_:), I lose the Array type information of ArrayT.

Shouldn't ArrayT definitely be an Array and therefore a member of the Sequence protocol, exposing the map function? 🤔

the working solution I'm employing now is to just define a generic typealias outside of the protocol:

typealias ProtocolArray<ProtocolType: Protocol> = Array<ProtocolType.T>

struct Struct<ProtocolType: Protocol> {

    func doSomething(with: ProtocolArray<ProtocolType>) {
        let _ = with.map { $0 } // no complaints
    }
}

what am I missing here?

Upvotes: 2

Views: 891

Answers (2)

kennytm
kennytm

Reputation: 523344

The line associatedtype ArrayT = Array<T> only tells the compiler that the default value of ArrayT is Array<T>. An adaption of the protocol can still change ArrayT like:

struct U: Protocol {
    typealias T = UInt32
    typealias ArrayT = UInt64   // <-- valid!
}

If you want a fixed type, you should use a typealias...

// does not work yet.
protocol Protocol {
    associatedtype T
    typealias ArrayT = Array<T>
}

But the compiler complains that the type is too complex 🤷. So the best you could do is constrain the ArrayT to be a Sequence / Collection / etc, and hope that the adaptors won't change the type themselves.

// still somewhat off
protocol Protocol {
    associatedtype T
    associatedtype ArrayT: Sequence = [T]
}

Note that, however, the Sequence can have any element type, but we want ArrayT's Element must be T. We cannot attach a where clause to the associatedtype:

// fail to compile: 'where' clause cannot be attached to an associated type declaration
associatedtype ArrayT: Sequence where Iterator.Element == T = [T]

Instead, you need to put this constraint every time you use the protocol:

struct Struct<ProtocolType: Protocol> 
    where ProtocolType.ArrayT.Iterator.Element == ProtocolType.T {

Complete, working code:

protocol Protocol {
    associatedtype T
    associatedtype ArrayT: Sequence = [T]
//                       ^~~~~~~~~~
}

struct Struct<ProtocolType: Protocol> 
    where ProtocolType.ArrayT.Iterator.Element == ProtocolType.T 
//  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
{
    func doSomething(w: ProtocolType.ArrayT) {
        let _: [ProtocolType.T] = w.map { $0 }
    }
}

Upvotes: 2

BaseZen
BaseZen

Reputation: 8718

When you "initialize" an associatedtype, you're not defining it. That's not the point of associated types in the first place. Associated types are deliberately unbound ("placeholders") until the adopting class resolves them all. All you're doing there is giving it a default value, which a conforming class is allowed to override. To extend your example:

protocol Protocol {
    associatedtype T
    associatedtype ArrayT = Array<Self.T>

    func useSomeT(myFavoriteT: T)

    func useSomeArrayT(myLeastFavoriteArrayT: ArrayT)
}

class Whatever: Protocol {
    func useSomeT(myFavoriteT: Int) {
        print("My Favorite Int: \(myFavoriteT)")
    }

    func useSomeArrayT(myLeastFavoriteArrayT: [Int: String]) {
        print(myLeastFavoriteArrayT.map { $0.1 })
    }
}

struct Struct<ProtocolType: Protocol> {
    func doSomething(stuff: ProtocolType.ArrayT) {
        print(type(of: stuff))
    }
}

let x = Struct<Whatever>()
x.doSomething(stuff: [3: "Doggies"])

For your example, it seems all you really want is to declare with: Array<ProtocolType.T> (or simply with: [ProtocolType.T]) as the parameter type.

Upvotes: 2

Related Questions