Mutually Recursive Protocols / Compiler Crash

protocol Person : Hashable {
  var fullname : String { get set }
  
  typealias R : Relationship
  var relationships : Array<R> { get }
}

protocol Relationship : Hashable {
  typealias P : Person
  var source : P { get }
  var target : P { get }
}


The above causes a compiler crash; surely it is a known issue? Is there a work-around? Is the above a coding-style that is to be avoided (in a language like Swift)?


[Apple Swift version 2.0 (swiftlang-700.0.52.2 clang-700.0.65)

Target: x86_64-apple-darwin14.4.0]

Replies

Don't know if there's a syntactically valid way to do it, but does it work if source and target are weak or unowned?


Edit: I thought maybe the compiler was trying to save you from a reference cycle, but I tried it with both weak and unowned, and no difference. Splitting <Person> into two protocols compiles, though.

protocol Person : Hashable {
    var fullname : String { get set }
}
protocol PersonInRelationship : Person {
    typealias R : Relationship
    var relationships : Array<R> { get }
}
protocol Relationship : Hashable {
    typealias P : Person
    var source : P { get }
    var target : P { get }
}

Compiler should not crash and I don't know if this case matches any of the known issues. You should send a Bug Report.

Is the above a coding-style that is to be avoided (in a language like Swift)?

I'm afraid the answer for this question is Yes.


When Swift applies a protocol as a type constraint, it needs to infer the actual types of the associated types.

So, when you use Person protocol, Person.R needs to be inferred.

And, any arbitrary type conforming to Relationship can be Person.R, Swift needs to infer Person.R.P,

two types conforming to Person and Person.R.P may be different, so this makes an inifinite recursion, not mutual recursion:

Person.R.P, Person.R.P.R, Person.R.P.R.P, ...


You may already know, if you remove Hashable, you can write something like this:

protocol Relationship {
    var source : Person { get }
    var target : Person { get }
}
protocol Person {
    var fullname : String { get set }

    var relationships: [Relationship] {get}
}

If you want to make all actual implementation of Person and Relationship to be Hashable, you can make derived protocols and use them:

protocol PersonType: Person, Hashable {}
protocol RelationshipType: Relationship, Hashable {}


One more, you should consider if both type may be value types, when you define mutually dependent types.

I asked specifically about 'coding style' because there is something about my modelling style that causes me to hit these issues _every_ time.


If I don't expose `var Relationship : Set<Relationship>` and instead add methods like `addFriend`, `mapFriends`, `mapRivals`, etc (all the different facets of a Relationship - or addRelationship:as:) then the problem is avoided - Relationship becomes an implementation detail of Person. That is workable; maybe I learn something about modeling from that.


Regarding the 'infinite recursion', is there not something like a `where` clause on a `typealias`?


protocol Person : Hashable {
  var fullname : String { get set }

  typealias R : Relationship where R.P == Self
  var relationships : Set<R> { get }
}

No longer crashes in: Apple Swift version 2.0 (700.0.57 700.0.72). Issues a warning of 'Type may not reference itself as a requirement'.

Having `where` constraint in `typealias` of protocols seems to be a possible extension for me, you'd better write a feature request.


I'm not sure the feature make this case easier, but it can be a good tool to represent our idea more straightforward.

And maybe we need some more good tools to use Swift as a really Protocol Oriented Programming language.

there is a warning but not a solution

I want to use it for VIPER, and the type alias conclict is on the View, Presenter and Interactor


protocol ViewControllerProtocol: class {
    typealias PresenterType:PresenterProtocol
    var presenter:PresenterType! {get set}
}
protocol PresenterProtocol: class {
    typealias InteractorType: InteractorProtocol
    typealias ViewControllerType: ViewControllerProtocol

    weak var view: ViewControllerType! {get set}
    var interactor: InteractorType! {get set}
    init()
}
protocol InteractorProtocol:class {
    typealias PresenterType: PresenterProtocol

    weak var presenter:PresenterType! {get set}
    init()
}



i get the "Type may not reference itself as a requirement" error so dont know how to do it


if I erase the typealias type doesent work to use the Routing as generic and ad a func

presenter.interactor = newInteract
 loginPresenter.routing = self
 loginPresenter.view = viewController