27

I'm attempting to do the following in Swift:

protocol ProtocolWithAlias {
    typealias T
}

protocol AnotherProtocol {
    func someFunc() -> ProtocolWithAlias
}

But I get the error: Protocol 'ProtocolWithAlias' can only be used as a generic constraint because it has Self or associated type requirements.

Is it possible to do something like this? The error message (or at least the "only be used as a generic constraint" part) doesn't seem to make much sense to me.

I'm using the latest Xcode 6 beta 3.

Thanks!

Danyal Aytekin
  • 4,106
  • 3
  • 36
  • 44
MatthewSot
  • 3,516
  • 5
  • 39
  • 58

2 Answers2

23

Try this:

func someFunc<T:ProtocolWithAlias>() -> T
Catfish_Man
  • 41,261
  • 11
  • 67
  • 84
  • would you happen to know why it's done like that? It seems like a rather weird syntax, but maybe that's just because I'm used to C# :) – MatthewSot Jul 12 '14 at 02:02
  • 8
    The problem is that the type system doesn't know what to plug in for the associated type if you just specify the protocol, so instead you make a generic function that can be specialized to return any particular concrete type that conforms to the protocol, but not just "the protocol". The compiler should arguably be able to figure this out, but it can't currently. – Catfish_Man Jul 12 '14 at 02:12
  • 2
    nice but how to specify this generic protocol to a instance variable of a class ? – Dragouf Jul 03 '15 at 14:11
  • @Dragouf Say you have a protocol "G" with a generic type "T," and you want to add the protocol as a variable to class "C." The declaration of the class "C" would be "class C" and the instance variable would be of type "U" – Zag Aug 27 '15 at 15:13
  • @Zag I assume by "generic type" you mean "associated type". In this case, class C would error: "Cannot use associated type 'T' outside of it's protocol". – David James Oct 01 '15 at 13:48
  • There's a proposal for a way to fix this going through swift-evolution now: https://github.com/austinzheng/swift-evolution/blob/az-existentials/proposals/XXXX-enhanced-existentials.md , essentially it adds support for func someFunc() -> Any – Catfish_Man May 24 '16 at 17:44
13

It's possible to implement this by inverting the control: instead of returning a value from someFunc we pass a consumer that can accept any type implementing ProtocolWithAlias and do something with it.

protocol ProtocolWithAlias {
    typealias T
}

protocol ProtocolConsumer {
    func consume<T: ProtocolWithAlias>(value: T)
}

protocol AnotherProtocol {
    func someFunc(consumer: ProtocolConsumer)
}

This trick is known as transforming the function to continuation passing style (CPS). Unfortunately I couldn't find any way to implement this without CPSing. The type system feature we are looking is existential types (and this thread has a nice explanation), but I think Swift doesn't support them (yet).


Why is the other answer not correct? What this signature tells:

func someFunc<T:ProtocolWithAlias>() -> T

is that this function can return a value of type T for any type implementing ProtocolWithAlias that the caller chooses, but we wanted it to be chosen by the callee.

It's not even possible to write a sensible implementation of this function. Lets pretend I have an implementation of someFunc: I could create a new class implementing ProtocolWithAlias and request someFunc to somehow create an instance of this class:

class Uninhabited: ProtocolWithAlias {
    typealias T = Int
    init(nope: Uninhabited) {}
}

...

let impossible: Uninhabited = someFunc<Uninhabited>()
Paweł Nowak
  • 143
  • 1
  • 6