Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

protocol associated type typealias assignment compile error

Following code:

protocol SomeProtocol {
    typealias SomeType = Int // used typealias-assignment

    func someFunc(someVar: SomeType)
}

class SomeClass: SomeProtocol {
    func someFunc(someVar: SomeType) {
        print(someVar)
    }
}

gives compile-time error:

Use of undeclared type 'SomeType'

Adding, say typealias SomeType = Double, to the SomeClass resolves the error.

The question is, what's the point of typealias-assignment part (which is optional btw) of protocol associated type declaration though?

like image 868
mesmerizingr Avatar asked Jul 22 '15 11:07

mesmerizingr


2 Answers

In this case the assignment of Int to the typealias is equal to no assignment because it gets overridden by your conforming type:

// this declaration is equal since you HAVE TO provide the type for SomeType
protocol SomeProtocol {
    typealias SomeType

    func someFunc(someVar: SomeType)
}

Such an assignment provides a default type for SomeType which gets overridden by your implementation in SomeClass, but it is especially useful for protocol extensions:

protocol Returnable {
    typealias T = Int // T is by default of type Int
    func returnValue(value: T) -> T
}

extension Returnable {
    func returnValue(value: T) -> T {
        return value
    }
}

struct AStruct: Returnable {}

AStruct().returnValue(3) // default signature: Int -> Int

You get the function for free only by conforming to the protocol without specifying the type of T. If you want to set your own type write typealias T = String // or any other type in the struct body.

Some additional notes about the provided code example

You solved the problem because you made it explicit which type the parameter has. Swift also infers your used type:

class SomeClass: SomeProtocol {
    func someFunc(someVar: Double) {
        print(someVar)
    }
}

So SomeType of the protocol is inferred to be Double.

Another example where you can see that SomeType in the class declaration doesn't refer to to the protocol:

class SomeClass: SomeProtocol {
    typealias Some = Int
    func someFunc(someVar: Some) {
        print(someVar)
    }
}

// check the type of SomeType of the protocol
// dynamicType returns the current type and SomeType is a property of it
SomeClass().dynamicType.SomeType.self // Int.Type
// SomeType gets inferred form the function signature

However if you do something like that:

protocol SomeProtocol {
    typealias SomeType: SomeProtocol

    func someFunc(someVar: SomeType)
}

SomeType has to be of type SomeProtocol which can be used for more explicit abstraction and more static code whereas this:

protocol SomeProtocol {
    func someFunc(someVar: SomeProtocol)
}

would be dynamically dispatched.

like image 109
Qbyte Avatar answered Nov 08 '22 21:11

Qbyte


There is some great information in the documentation on "associated types" in protocols.

Their use is abundant throughout the standard library, for an example reference the SequenceType protocol, which declares a typealias for Generator (and specifies that it conforms to GeneratorType). This allows the protocol declaration to refer to that aliased type.

In your case, where you used typealias SomeType = Int, perhaps what you meant was "I want SomeType to be constrained to Integer-like behavior because my protocol methods will depend on that constraint" - in which case, you may want to use typealias SomeType: IntegerType in your protocol, and then in your class go on to assign a type to that alias which conforms to IntegerType.

UPDATE

After opening a bug w/ Apple on this and having had extensive discussion around it, I have come to an understanding of what the base issue is at the heart of this:

when conforming to a protocol, you cannot directly refer to an associated type that was declared only within that protocol

(note, however, that when extending a protocol the associated type is available, as you would expect)

So in your initial code example:

protocol SomeProtocol {
    typealias SomeType = Int
    func someFunc(someVar: SomeType)
}

class SomeClass: SomeProtocol {
    func someFunc(someVar: SomeType) {  // use of undeclared type "SomeType"
        print(someVar)
    }
}

...the error re: "use of undeclared type" is correct, your class SomeClass has not declared the type SomeType

However, an extension to SomeProtocol has access to the associated type, and can refer to it when providing an implementation:

(note that this requires using a where clause in order to define the requirement on the associated type)

protocol SomeProtocol {
    typealias SomeType = Int
    func someFunc(someVar: SomeType)
}

extension SomeProtocol where SomeType == Int {
    func someFunc(someVar: SomeType) {
        print("1 + \(someVar) = \(1 + someVar)")
    }
}

class SomeClass: SomeProtocol {}

SomeClass().someFunc(3)  // => "1 + 3 = 4"
like image 21
fqdn Avatar answered Nov 08 '22 19:11

fqdn