protocol Abstraction {
associatedtype T
associatedtype On: T
associatedtype Off: T
}
extension Abstraction {
var isOn: Bool {
....
}
var abstraction: T {
return isOn ? on : off
}
}
I want to assert that On & Off both conform to the same protocol, the above does not work because it cannot determine that T is of type protocol.
Is this something like this possible?
I can work around it by not providing any T conformance and implementing it as an extension with the concrete types, but this is annoying and has to be repeated for every type even though the core logic is the same:
extension Abstraction where On: Printer, Off: Printer {
var abstraction: Printer {
return isOn ? on : off
}
}
Here's a more concrete example, I want to guarantee that both On and Off conform to the same protocol so I am able to have the property var abstract: Abstraction which avoids me putting if isOn throughout the code. Hope this clears up your understanding.
import Foundation
protocol Abstraction {
associatedtype Abstraction
associatedtype On
associatedtype Off
var isOn: Bool { get set }
var key: String { get }
var on: On { get }
var off: Off { get }
}
extension Abstraction {
var key: String {
return String(describing: self)
}
var isOn: Bool {
get { return UserDefaults.standard.bool(forKey: key) }
set { UserDefaults.standard.set(newValue, forKey: key) }
}
var abstract: Abstraction { // <-- To avoid littering `if isOn` throughout
return isOn ? on : off
}
}
protocol Printer {
func print(_ string: String)
}
struct SwiftPrinter: Printer {
func print(_ string: String) {
Swift.print(string)
}
}
struct LoggerPrinter: Printer {
func print(_ string: String) {
Logger.log(string)
}
}
class PrinterAbstraction: Printer, Abstraction {
typealias Abstraction = Printer
typealias On = SwiftPrinter
typealias Off = LoggerPrinter
lazy var on: On = .init()
lazy var off: Off = .init()
func print(_ string: String) {
abstract.print(string)
}
}
AFAICS the protocol Abstraction really has no use for any more specific type than the protocol/class of the associated type that you (somewhat confusingly also) call Abstraction, ie on and off don't need their separate concrete types represented there, they can simply both have their common supertype Abstraction, because they are never used as anything more specific than Abstraction. So:
protocol AbstractionSwitch { // <-- I don't know about this renaming-attempt but anyway
associatedtype Abstraction
var on: Abstraction
var off: Abstraction
...
}
Note that on and off can still be different types (dynamically), as long as they can both be stored as their common supertype Abstraction.
I rewrote your last example into a working program, using a generic struct AbstractionSwitch<Abstraction> instead of your protocol Abstraction:
import Foundation
struct AbstractionSwitch<Abstraction> {
var on: Abstraction
var off: Abstraction
var key: String
var isOn: Bool {
get { return UserDefaults.standard.bool(forKey: key) }
set { UserDefaults.standard.set(newValue, forKey: key) }
}
var abstraction: Abstraction { // <-- To avoid littering `if isOn` throughout
return isOn ? on : off
}
init(key: String, on: Abstraction, off: Abstraction) {
self.key = key
self.on = on
self.off = off
}
}
struct FakeLogger {
static func log(_ str: String) { print("fakelogged:", str) }
}
protocol Printer {
func print(_ string: String)
}
struct SwiftPrinter: Printer {
func print(_ string: String) {
Swift.print(string)
}
}
struct LoggerPrinter: Printer {
func print(_ string: String) {
FakeLogger.log(string)
}
}
var a = AbstractionSwitch<Printer>(key: "printer",
on: SwiftPrinter(),
off: LoggerPrinter())
a.isOn = true
a.abstraction.print("Hello world!") // Hello world!
a.isOn = false
a.abstraction.print("Hello world!") // fakelogged: Hello world!
Not at all, the above example you provided is something I neglected to consider (I assumed I could do it all using protocols) - but it looks like that will solve my dilemma - thanks for the help.