Protocol Associated Type Typealias Assignment Compile Error

protocol associated type typealias assignment compile error

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.

return protocol with associated type

In this function

func sendB_<T: BProtocol>() -> T{
return B() as! T
}

you cannot return a B as a T because the person who uses the function defines what T is, not you and T can be any type that conforms to Protocol For example, I could do this:

class C: BProtocol 
{
typealias B = Float
}

let c: C = Main().sendB_()

By doing that, I am setting T to be a C and the forced typecast in sendB_() will fail.

Unfortunately, protocols with associated types cannot, themselves, be treated like a concrete type, so the approach you took with AProtocol will not work.

As I see it, you have two options. Change your function return type to B. After all, you always do return a B

func sendB_() -> B {
return B()
}

If you want to keep it generic, try

protocol BProtocol 
{
associatedtype B

init() // Needed to be able to do T() in generic function
}

func sendB_<T: BProtocol>() -> T{
return T()
}

You need to add the initialiser to the protocol to make sure that an instance of type T always exists.

Is there information somewhere on Swift protocol associatedtype using `=` versus `:`?

The = and the : are two independent parts of an associated type declaration, rather than being mutually exclusive. This is the full syntax of protocol associated type declaration:

attributes(opt) 
access-level-modifier(opt)
'associatedtype'
typealias-name
type-inheritance-clause(opt)
typealias-assignment(opt)
generic-where-clause(opt)

The : TypeName part is the type-inheritance-clause, and the = TypeName is the typealias-assignment.

: TypeName constrains what type the associated type can be, namely that it must inherit/conform to TypeName. This is why : SomeManagerDelegate didn't work in your case. You are saying that SomeManager.DelegateType must be some kind of SomeManagerDelegate, but for Manager, this is not true - Manager.delegate is of type ManagerDelegate, which is a totally unrelated protocol. Even if it were SomeManagerDelegate, it wouldn't work either because protocols don't conform to themselves.

= TypeName sets a default type for the associated type. If the compiler cannot infer what the type the associated type for a conformance should be and you didn't say it explicitly it either, it will use that type instead. But in your case, this fact didn't really matter. What actually caused your code to work, wasn't the addition of = SomeManagerDelegate, but the removal of the constraint : SomeManagerDelegate. You are no longer constraining what type the associated type should be (it can be anything!), so for Manager, the associated type can now be inferred to be ManagerDelegate. Note that you don't have to explicitly say:

typealias DelegateType = ManagerDelegate

In fact, you can totally remove = SomeManagerDelegate and just say:

associatedtype DelegateType

so it is far from the truth that the = is "the only thing keeping it together".

This = TypeName syntax doesn't seem very well documented. Here's a related Swift forums post.

How associated(typealias) type and Self in protocols work?

Pro1

Writing this

protocol Pro1 {
typealias Element
}

you are just telling that there will be a type named Element.

Pro2

Adding this

protocol Pro2: Pro1 {
typealias Element = Self
}

you are telling to the compiler that Element will be the same type of the type that is implementing Pro2.

So yes, there is a relation between the Element in Pro1 and Pro2.

Adding methods to Pro1

Let's declare 2 methods that will use Element in Pro1

protocol Pro1 {
typealias Element
func a() -> Element
func b(elm: Element)
}

Conforming to Pro1

Now a class conform to Pro1 will be like this.

class Foo: Pro1 {
func a() -> String {
return ""
}
func b(elm: String) {
}
}

As you can see we are forced by the compiler to set the return type of a and the param of b to be of the same type.

Conforming to Pro2

Now let's try to conform another class to Pro2. Again Pro1 will force us to declare methods a and b where the return type of a is equals to the param of b.
Furthermore Pro2 will force us to set this type equals to the type of the current type Boo.

So the previous class will to conform to Pro2 because String is different from Foo

class Foo: Pro2 {
func a() -> String { // <- compiler error
return ""
}
func b(elm: String) { // <- compiler error

}
}

But if we declare a new class and replace Element with Boo it will work because the constraints of both protocols are satisfied.

class Boo: Pro2 {
func a() -> Boo {
return Boo()
}
func b(elm: Boo) {

}
}

Swift: Question on Generic Functions and Associated Types

You are getting this error, because indeed self does not match all given constraints. By stating func test<M: Model>(model: M) -> M where M.ModelType == T you only say that this type M (which is only exists in the context of a call to your test function) is to be a Model, which's ModelType is the same as the ModelType of self. That does not mean, however, that Self and M are equivalent.

A little example using your types from above:

Assuming self is an instance of Implementation<Int>. In the context of a call to test(model:), M could be either of Implementation<Int> or Alternative<Int>. Returning self wouldn't be a problem in the first case, as both instances have the same type. However, in the second case you could assign an Implementation<Int> to an Alternative<Int>.

var a = Alternative<Int>()
let x = Implementation<Int>()
// This would assign something of type `Implementation<Int>` to a variable that
// may only contain `Alternative<Int>`.
var a = x.test(model: a)

I am sure there is a method to do what you want to achieve in Swift, but the solution totally depends on what that is.

When using typealias, I get the compiler error use of undeclared type

You can use typealias to make it easier for you declaring more complex types for example:

typealias RGBA = (red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat)

let redColor: RGBA = (1.0, 0.0, 0.0, 1.0)

redColor.red // 1
redColor.green // 0
redColor.blue // 0
redColor.alpha // 1

typealias CMYK = (cyan: CGFloat, magenta: CGFloat, yellow: CGFloat, black: CGFloat)

let cyanColor: CMYK = (1.0, 0.0, 0.0, 0.0)

cyanColor.cyan // 1
cyanColor.magenta // 0
cyanColor.yellow // 0
cyanColor.black // 0

Cannot assign class instance to its protocol type?

First, you want to read the canonical thread on this from devforums. You particularly want to skip around to read jckarter's comments.

Now to the edited question:

let a = s.shortTrain() //ERROR: Cannot convert the expression's type '()' to type 'Train'

This is because you haven't given the compiler enough information to determine the type of a. Think through what it sees:

func shortTrain<ShortType: Train where ShortType.CarriageType == CarriageType>() -> ShortType {
let a = s.shortTrain()

The compiler needs to figure out the type of a at compile time, and it can't handle abstract types. It needs a fully specified type for ShortType (everything nailed down; all the generics specified, all the type aliases resolved). It looks around, and it sees some constraints on ShortType, but it doesn't see anything that actually gives a type. All it has is a, which isn't giving it any hints.

So unfortunately, that leaves you having to tell it explicitly what you want to happen.

let a: SimpleTrain<String> = s.shortTrain()

That's probably the opposite of what you were going for, but it's about all you can do in Swift right now. The Swift team has indicated (many times) that they are well aware of these issues with associated types (and several other related weaknesses in the type system). They're specifically aware of the Scala type system which can handle these things and has a lot in common with the current Swift type system (though in my experience, getting complex path-dependent associated types to work in Scala can also lead to hair tearing).

That said, it's not exactly obvious from your example what you planned to do with this functionality. Would some Trains return a different type as their shortTrain()?

I find that these problems often blow up in the general case, but tend to be quite solvable in the specific cases for the app in front of you. It's hard to build really arbitrary types in Swift in code that could solve every problem, but it often works out when you focus on the types you'll really need. For example, if shortTrain() returned Self, this obviously gets simpler. If the caller knows the desired resulting type, then an init(shorten:) can probably handle it. A protocol method like shortCarriages() -> [CarriageType] may provide a good bridge. Stay flexible on your design, and one of them will almost certainly work out.

Error trying to return protocol associated type from method

Just do what the error says - use SearchApiResource as a generic parameter base.

func searchForItem<T: SearchApiResource>(resource: T, searchTerm: String, pageNumber: Int, completion: @escaping (T.ModelType) -> Void ) {
}


Related Topics



Leave a reply



Submit