Protocol conformance reuse

Given the following:

protocol ProtocolA {
//lengthy list of variables and funcs
}

protocol ProtocolB {}

and I also have

class A: ProtocolA {
//imagine this is a tediously long and complex confomance to ProtocolA
}

class B: ProtocolA, ProtocolB {
//I don't want to have to copy and paste from A, we already have it implemented.
}

If Class A has a perfectly good implementation of ProtocolA that i'd like to reuse in class B, I'm going to have to write out the whole thing again. Obviously I understand I can have a default implementation of a protocol, but I might only want to use it in specific classes. I assume there's a very good reason we can't add a keyword to do something like:

class B: ProtocolA by ClassA, ProtocolB {
//copied conformance from ClassA - synthesised by compiler

//protocolB conformance
}

But i'm intersted as to know why, is it because there's low value in this since the only benefit would just be readability and it's a lot harder to do than I think?

If B has the same properties, methods and implementations of A, maybe what you're looking for is just subclassing:

class B: A, ProtocolB {}

What if ClassB is already a subclass of something else? I'm blocked because of multiple inheritance.

One possible setup is to do something like this:

protocol P {
  func complexRequirement()
}

protocol SpecialCaseP: P {}

extension SpecialCaseP {
  func complexRequirement() {
    print("That was easy")
  }
}

class A: SpecialCaseP {}
class B: SpecialCaseP {}

func test<T: P>(t: T) {
  t.complexRequirement()
}

test(t: A()) // That was easy
test(t: B()) // That was easy

Depending on how else you plan to use these classes this could be problematic (e.g., good ol' SR-103), but to a first-order approximation I think this satisfies your constraints.

3 Likes

Ah, Yes. I think i formulated the question a bit poorly. That would satisfy the contraints, but if i wanted to be more general and any class can point to any implementation, I want to do the same thing as a default but instead class A has provided it. i.e:

protocol P {
  func complexRequirement()
}

class A: P {
  func complexRequirement() {
    print("That was easy")
  }
}
class B: P  {} with some way to tell the compiler to just reuse the implementation A has

class C: P {
  var someVarIDontWantInD
  func complexRequirement() {
    print("Not so easy")
  }
}

class D {
var name: String = "D"
}

class E: D, P *with some way to tell the compiler to reuse the implementation C has*

func test<T: P>(t: T) {
  t.complexRequirement()
}

test(t: A()) // That was easy
test(t: B()) // That was easy
test(t: C()) // Not so easy
test(t: E()) // Not so easy

Obviously the solutuon is I can just copy and paste the func into E from C or into B from A so this is really just some sugar that allows us to nicely reference an already existing implmentation (does a similar thing happen for defaults?) The finer details of the functioning of Swift are far beyond my abilities, is there a constraint in how the language works that prevents this capability from being added? Or is it just not really something anyone in the community sees value in?

1 Like

I don't really understand why the protocol-based solution doesn't work for your extended example:

protocol P {
  func complexRequirement()
}

protocol SpecialCaseP: P {}
extension SpecialCaseP {
  func complexRequirement() {
    print("That was easy")
  }
}

protocol HardToHandleP: P {}
extension HardToHandleP {
  func complexRequirement() {
    print("Not so easy")
  }
}

class A: SpecialCaseP {}
class B: SpecialCaseP {}
class C: HardToHandleP {
    var someVarIDontWantInD: Int = 0
}
class D {
  var name = "D"
}
class E: D, HardToHandleP {}

func test<T: P>(t: T) {
  t.complexRequirement()
}

test(t: A()) // That was easy
test(t: B()) // That was easy
test(t: C()) // Not so easy
test(t: E()) // Not so easy

Is this what you're aiming for, or have I still misunderstood?

In the general case, it's not so simple to just say "reuse the implementation from this other existing type"—what if the existing implementation references other members that aren't part of the protocol definition? Should those members be copied over as well? What if it references members that have the same name as members of the target conformer? Do we reuse those members, or is that an error? What if the implementation references private members?

Factoring out the common implementation into a protocol resolves all these issues, because the protocol defines what is considered "part of the implementation" that should be copied, and the visibility rules are well-established based on the scoping of the protocol and the extension itself.

1 Like

Yeah thats a great answer thank you, should have been more obvious to me we can't do it because a func can reference members that don't exist in the other class and how the protocol inheritance fixes it.

1 Like
Terms of Service

Privacy Policy

Cookie Policy