Default protocol implementation takes precedence over the subclass's implementation [duplicate] - swift

I'm experimenting with Swift protocol extensions and I found this quite confusing behaviour. Could you help me how to get the result I want?
See the comments on the last 4 lines of the code. (You can copy paste it to Xcode7 playground if you want). Thank you!!
protocol Color { }
extension Color { var color : String { return "Default color" } }
protocol RedColor: Color { }
extension RedColor { var color : String { return "Red color" } }
protocol PrintColor {
func getColor() -> String
}
extension PrintColor where Self: Color {
func getColor() -> String {
return color
}
}
class A: Color, PrintColor { }
class B: A, RedColor { }
let colorA = A().color // is "Default color" - OK
let colorB = B().color // is "Red color" - OK
let a = A().getColor() // is "Default color" - OK
let b = B().getColor() // is "Default color" BUT I want it to be "Red color"

The short answer is that protocol extensions don't do class polymorphism. This makes a certain sense, because a protocol can be adopted by a struct or enum, and because we wouldn't want the mere adoption of a protocol to introduce dynamic dispatch where it isn't necessary.
Thus, in getColor(), the color instance variable (which may be more accurately written as self.color) doesn't mean what you think it does, because you are thinking class-polymorphically and the protocol is not. So this works:
let colorB = B().color // is "Red color" - OK
...because you are asking a class to resolve color, but this doesn't do what you expect:
let b = B().getColor() // is "Default color" BUT I want it to be "Red color"
...because the getColor method is defined entirely in a protocol extension. You can fix the problem by redefining getColor in B:
class B: A, RedColor {
func getColor() -> String {
return self.color
}
}
Now the class's getColor is called, and it has a polymorphic idea of what self is.

There are two very different issues at play here: The dynamic behavior of protocols and the resolution of protocol "default" implementations.
On the dynamic front, we can illustrate the problem with a simple example:
protocol Color { }
extension Color {
var color: String { return "Default color" }
}
class BlueBerry: Color {
var color: String { return "Blue color" }
}
let berry = BlueBerry()
print("\(berry.color)") // prints "Blue color", as expected
let colorfulThing: Color = BlueBerry()
print("\(colorfulThing.color)") // prints "Default color"!
As you point out in your answer, you can get the dynamic behavior if you define color as part of the original Color protocol (i.e. thereby instructing the compiler to reasonably expect the conforming classes to implement this method and only use the protocol's implementation if none is found):
protocol Color {
var color: String { get }
}
...
let colorfulThing: Color = BlueBerry()
print("\(colorfulThing.color)") // now prints "Blue color", as expected
Now, in your answer, you question why this falls apart a bit when B is a subclass of A.
I think it helps to remember that the method implementations in protocol extensions are "default" implementations, i.e. implementations to be used if the conforming class doesn't implement it, itself. The source of the confusion in your case comes from the fact that B conforms to RedColor which has a default implementation for color, but B is also a subclass of A which conforms to Color, which has a different default implementation of color.
So, we might quibble about Swift's handling of this situation (personally I'd rather see a warning about this inherently ambiguous situation), but the root of the problem, in my mind, is that there are two different hierarchies (the OOP object hierarchy of subclasses and the POP protocol hierarchy of protocol inheritance) and this results in two competing "default" implementations.
I know this is an old question, so you've probably long since moved on to other things, which is fine. But if you're still struggling regarding the right way to refactor this code, share a little about what this class hierarchy and what this protocol inheritance actually represent and we might be able to offer more concrete counsel. This is one of those cases where abstract examples just further confuse the issue. Let's see what the types/protocols really are. (If you've got working code, http://codereview.stackexchange.com might be the better venue.)

I managed to get it working by defining color on Color and switching the implementation list for B. Not much good if B must be an A though.
protocol Color {
var color : String { get }
}
protocol RedColor: Color {
}
extension Color {
var color : String {
get {return "Default color"}
}
}
extension RedColor {
var color : String {
get {return "Red color"}
}
}
protocol PrintColor {
func getColor() -> String
}
extension PrintColor where Self: Color {
func getColor() -> String {
return color
}
}
class A : Color, PrintColor {
}
class B : RedColor, PrintColor {
}
let a = A().getColor() // "Default color"
let b = B().getColor() // "Red color"

I came across this problem whilst trying to implement an "optional" method through a protocol. It can be made to work, in structs, in classes that do not inherit, and also in classes that inherit from a base that implements a non-protocol-default method which can be overridden. The only case that doesn't work is a class that inherits from a base that declares conformity but doesn't provide its own "non-default" implementation - in that case the protocol extension's default is "baked-in" to the base class, and cannot be overridden or re-defined.
Simple example:
typealias MyFunction = () -> ()
protocol OptionalMethod {
func optionalMethod() -> MyFunction?
func executeOptionalMethod()
}
extension OptionalMethod {
func optionalMethod() -> MyFunction? { return nil }
func executeOptionalMethod() {
if let myFunc = self.optionalMethod() {
myFunc()
} else {
print("Type \(self) has not implemented `optionalMethod`")
}
}
}
class A: OptionalMethod {
}
class B: A {
func optionalMethod() -> MyFunction? {
return { print("Hello optional method") }
}
}
struct C: OptionalMethod {
func optionalMethod() -> MyFunction? {
return { print("Hello optionalMethod") }
}
}
class D: OptionalMethod {
func optionalMethod() -> MyFunction? {
return { print("Hello optionalMethod") }
}
}
class E: D {
override func optionalMethod() -> MyFunction? {
return { print("Hello DIFFERENT optionalMethod") }
}
}
/* Attempt to get B to declare its own conformance gives:
// error: redundant conformance of 'B2' to protocol 'OptionalMethod'
class B2: A, OptionalMethod {
func optionalMethod() -> MyFunction? {
return { print("Hello optional method") }
}
}
*/
class A2: OptionalMethod {
func optionalMethod() -> MyFunction? {
return nil
}
}
class B2: A2 {
override func optionalMethod() -> MyFunction? {
return { print("Hello optionalMethod") }
}
}
let a = A() // Class A doesn't implement & therefore defaults to protocol extension implementation
a.executeOptionalMethod() // Type __lldb_expr_201.A has not implemented `optionalMethod`
let b = B() // Class B implements its own, but "inherits" implementation from superclass A
b.executeOptionalMethod() // Type __lldb_expr_205.B has not implemented `optionalMethod`
let c = C() // Struct C implements its own, and works
c.executeOptionalMethod() // Hello optionalMethod
let d = D() // Class D implements its own, inherits from nothing, and works
d.executeOptionalMethod() // Hello optionalMethod
let e = E() // Class E inherits from D, but overrides, and works
e.executeOptionalMethod() // Hello DIFFERENT optionalMethod
let a2 = A2() // Class A2 implements the method, but returns nil, (equivalent to A)
a2.executeOptionalMethod() // Type __lldb_expr_334.A2 has not implemented `optionalMethod`
let b2 = B2() // Class B2 overrides A2's "nil" implementation, and so works
b2.executeOptionalMethod() // Hello optionalMethod

Note: The proposed solution "Defining color as part of the original Color protocol" is not solving the problem when you have inheritance involved e.g.
RedBerry inherits from BlueBerry which conforms to protocol Color.
protocol Color {
var color: String { get }
}
extension Color {
var color: String { return "Default color" }
}
class BlueBerry: Color {
// var color: String { return "Blue color" }
}
class RedBerry: BlueBerry {
var color: String { return "Red color" }
}
let berry = RedBerry()
print(berry.color) // Red color
let colorfulThing: Color = RedBerry()
print(colorfulThing.color) // Actual: Default color, Expected: Red color

Related

why cant i return opaque types in my swift code? i am already returning a type conforming to that protocol [duplicate]

This question already has answers here:
What is the "some" keyword in Swift(UI)?
(14 answers)
Closed 1 year ago.
image
As showing in the image, i'm already using types such as MilkChocolate which conforms to the "Chocolate" protocal, and im trying to declare a function which returns a "some chocolate", which means return a type of chocolate depends on the input, but it gives me an error says
Function declares an opaque return type, but the return statements in its body do not have matching underlying types
this confused me by a lot and i think MiklChocolate is an underlying type for the Chocolate protocol
whats wrong?🤔
and my code should match this tutorial: https://docs.swift.org/swift-book/LanguageGuide/OpaqueTypes.html
PS: if you cant view the image, here's the code
protocol Chocolate {
associatedtype Content
var mass:Double {get}
}
struct Honey {}
struct Milk {}
struct Wine {}
class HoneyChocolate: Chocolate {
typealias Content = Honey
var mass = 1.0
}
class MilkChocolate: Chocolate {
typealias Content = Milk
var mass = 1.2
var mysteriousEffect = true
}
class WineChocolate: Chocolate {
typealias Content = Wine
var mass:Double = 999
}
func giveMeChocolate(of type:String) -> some Chocolate {
switch type {
case "honey":
return HoneyChocolate()
case "milk":
return MilkChocolate()
default:
return WineChocolate()
}
}
The problem occurs because Swift can't return different types of generic types. HoneyChocolate and WineChocolate are different types and that causes an issue.
A different way of achieving your goal is to return a class type of Chocolate.
First let's declare our enum flavors:
enum Flavor {
case Honey, Milk, Wine
}
then we will declare the superclass Chocolate:
class Chocolate {
var flavor: Flavor
var mass:Double
init(_ flavor: Flavor ,_ mass: Double) {
self.flavor = flavor
self.mass = mass
}
}
Now we can inherit this class:
class HoneyChocolate: Chocolate {
init() {
super.init(Flavor.Honey, 1.0)
}
}
class MilkChocolate: Chocolate {
init() {
super.init( Flavor.Milk, 1.2)
var mysteriousEffect = true
}
}
class WineChocolate: Chocolate {
init() {
super.init( Flavor.Wine, 999)
var mysteriousEffect = true
}
}
finally we can return our chocolates:
func giveMeChocolate(of type:Flavor) -> Chocolate {
switch type {
case Flavor.Honey:
return HoneyChocolate()
case Flavor.Milk:
return MilkChocolate()
default:
return WineChocolate()
}
}
Why some doesn't work
some allows you to return a generic type like and ONLY one type.
for example:
let's make a CandyChocolate class:
class CandyChocolate: Chocolate {
typealias Content = Candy
var mass = 1.0
var sweetness
init(sweetness: int) {
self.sweetness = sweetness
}
}
Now to return a some Chocolate we are allowed to return exactly one type of chocolate:
func giveMeChocolate(of type:String) -> some Chocolate {
switch type {
case "honey":
return CandyChocolate(sweetness: 5)
case "milk":
return CandyChocolate(sweetness: 3)
default:
return CandyChocolate(sweetness: 8)
}
}

Is there a way to constrain `Self` to a generic type?

https://www.raywenderlich.com/148448/introducing-protocol-oriented-programming
protocol Bird {
var name: String { get }
var canFly: Bool { get }
func doSomething()
}
protocol Flyable {
var airspeedVelocity: Double { get }
}
extension Bird {
// Flyable birds can fly!
var canFly: Bool { return self is Flyable }
func doSomething() {
print("default Bird: \(name)")
}
}
class FlappyBird: Bird, Flyable {
let name: String
let canFly = true
var airspeedVelocity: Double = 5.0
init(name: String) {
self.name = name
}
}
class Penguin: Bird {
let name: String
let canFly = false
init(name: String) {
self.name = name
}
}
class Owl<T> : Bird {
let name: String
let power: T
init(name: String, power: T) {
self.name = name
self.power = power
}
}
extension Bird where Self: FlappyBird {
func doSomething() {
print("FlappyBird: \(name)")
}
}
extension Bird where Self: Owl<String> {
func doSomething() {
print("Owl<String>: \(name)")
}
}
let fb = FlappyBird(name:"PAK")
let penguin = Penguin(name:"Mr. Pickle")
let nightOwl = Owl<String>(name:"Night Owl", power:"Who")
let dayOwl = Owl<Int>(name:"Day Owl", power: 50)
let birds: [Bird] = [fb, penguin, nightOwl, dayOwl]
birdloop: for bird in birds {
bird.doSomething()
}
The output I get is:
FlappyBird: PAK
default Bird: Mr. Pickle
default Bird: Night Owl
default Bird: Day Owl
The first result works as expected since
extension Bird where Self: FlappyBird {
func doSomething() {
print("FlappyBird: \(name)")
}
}
The second result works as expected since it calls the default protocol extension:
extension Bird {
// Flyable birds can fly!
var canFly: Bool { return self is Flyable }
func doSomething() {
print("default Bird: \(name)")
}
}
The third result I would expect to print
Owl<String>: Night Owl
since nightOwl is of type Owl<String>. But instead it calls the default protocol extension:
default Bird: Night Owl
Is there some reason why
extension Bird where Self: FlappyBird {
func doSomething() {
print("default Bird: \(name)")
}
}
is called for the FlappyBird type but
extension Bird where Self: Owl<String> {
func doSomething() {
print("Owl<String>: \(name)")
}
}
is not called for the Owl<String> type?
For the generic type Owl<T> you are allowed to have the constraint where Self: Owl<String>, but it will only work in contexts where the concrete type information is available.
To make it clear what is happening, consider this:
let nightOwl = Owl<String>(name: "Night Owl", power: "Who")
nightOwl.doSomething() // prints "Owl<String>: Night Owl"
As opposed to this:
let nightOwl: Bird = Owl<String>(name: "Night Owl", power: "Who")
nightOwl.doSomething() // prints "default Bird: Night Owl"
When Swift creates the protocol witness tables for the types Owl<T> and FlappyBird, it has to act differently for each one because Owl is generic. If it doesn't have the concrete type information, i.e. Owl<String> at the call site, it must use the default implementation for Owl<T>. This "loss" of type information is happening when you are inserting the owls into the array of type [Bird].
In the case of FlappyBird, since there is only one possible implementation (since it's not generic), the compiler produces a witness table with the "expected" method reference, which is print("FlappyBird: \(name)"). Since FlappyBird is not generic, its witness table doesn't need any reference to the unconstrained default implementation of doSomething() and can therefore correctly call the the constrained implementation even when the concrete type information is missing.
To make it clear that the compiler "needs" to have the fall back behavior for a generic type, you can remove the Bird conformance from Owl<T> and try to rely solely on the constrained default implementation. This will result in a compilation error with an error that is, as usual with Swift, highly misleading.
Value of type 'Owl' has no member 'doSomething'
Basically, it seems the witness table can't be built because it requires the existence of an implementation that will work for all types T on Owl.
References
https://forums.swift.org/t/swifts-method-dispatch/7228
https://developer.apple.com/videos/play/wwdc2016/416/
#AllenHumphreys answer here is a decent explanation on the why part.
As for the fix part, implement doSomething() for the generic Owl as:
class Owl<T> : Bird {
//...
func doSomething() {
print("Owl<\(type(of: power))>: \(name)")
}
}
and now you no longer need doSomething() in extension Bird where Self: Owl<String>

Is there some workaround to cast to a generic base class without knowing what the defined element type is?

I am trying to achieve a design where I can have a base class that has a generic property that I can change values on by conforming to a protocol.
protocol EnumProtocol {
static var startValue: Self { get }
func nextValue() -> Self
}
enum FooState: EnumProtocol {
case foo1, foo2
static var startValue: FooState { return .foo1 }
func nextValue() -> FooState {
switch self {
case .foo1:
return .foo2
case .foo2:
return .foo1
}
}
}
enum BarState: EnumProtocol {
case bar
static var startValue: BarState { return .bar }
func nextValue() -> BarState {
return .bar
}
}
class BaseClass<T: EnumProtocol> {
var state = T.startValue
}
class FooClass: BaseClass<FooState> {
}
class BarClass: BaseClass<BarState> {
}
Is it possible to end up with a solution similar to this where the element type is unknown and the value relies on the nextValue() method.
let foo = FooClass()
let bar = BarClass()
if let test = bar as? BaseClass {
test.state = test.state.nextValue()
}
This works but BarState will be unknown in my case and a lot of classes will be subclasses of BaseClass and have different state types.
let bar = BarClass()
if let test = bar as? BaseClass<BarState> {
test.state = test.state.nextValue()
}
This is a simplified example. In my case I will get a SKNode subclass that has a state property that is an enum with a nextvalue method that have defined rules to decide what the next value will be. I am trying to have a generic implementation of this that only relies on what is returned from the nextValue method. Is there a better pattern to achieve this?
This will not work for this exact scenario because EnumProtocol can not be used as concrete type since it has a Self type requirement, however, in order to achieve this type of behavior in other cases you can create a protocol that the base class conforms to and try to cast objects to that type when you are trying to determine if an object is some subclass of that type.
Consider the following example
class Bitcoin { }
class Ethereum { }
class Wallet<T> {
var usdValue: Double = 0
}
class BitcoinWallet: Wallet<Bitcoin> {
}
class EthereumWallet: Wallet<Ethereum> {
}
let bitcoinWallet = BitcoinWallet() as Any
if let wallet = bitcoinWallet as? Wallet {
print(wallet.usdValue)
}
This will not work, due to the same error that you are referring to:
error: generic parameter 'T' could not be inferred in cast to 'Wallet<_>'
However, if you add the following protocol
protocol WalletType {
var usdValue: Double { get set }
}
and make Wallet conform to that
class Wallet<T>: WalletType
then you can cast values to that protocol and use it as expected:
if let wallet = bitcoinWallet as? WalletType {
print(wallet.usdValue)
}

How to define initializers in a protocol extension?

protocol Car {
var wheels : Int { get set}
init(wheels: Int)
}
extension Car {
init(wheels: Int) {
self.wheels = wheels
}
}
on self.wheels = wheels i get the error
Error: variable 'self' passed by reference before being initialized
How can I define the initializer in the protocol extension?
As you can see this doesn't work under these circumstances because when compiling, one has to make sure that all properties are initialized before using the struct/enum/class.
You can make another initializer a requirement so the compiler knows that all properties are initialized:
protocol Car {
var wheels : Int { get set }
// make another initializer
// (which you probably don't want to provide a default implementation)
// a protocol requirement. Care about recursive initializer calls :)
init()
init(wheels: Int)
}
extension Car {
// now you can provide a default implementation
init(wheels: Int) {
self.init()
self.wheels = wheels
}
}
// example usage
// mark as final
final class HoverCar: Car {
var wheels = 0
init() {}
}
let drivableHoverCar = HoverCar(wheels: 4)
drivableHoverCar.wheels // 4
As of Xcode 7.3 beta 1 it works with structs as expected but not with classes since if they are not final the init(wheels: Int) in the protocol is a required init and it can be overridden therefore it cannot be added through an extension. Workaround (as the complier suggests): Make the class final.
Another workaround (in depth; without final class)
To work with classes without making them final you can also drop the init(wheels: Int) requirement in the protocol. It seems that it behaves no different than before but consider this code:
protocol Car {
var wheels : Int { get set }
init()
// there is no init(wheels: Int)
}
extension Car {
init(wheels: Int) {
self.init()
print("Extension")
self.wheels = wheels
}
}
class HoverCar: Car {
var wheels = 0
required init() {}
init(wheels: Int) {
print("HoverCar")
self.wheels = wheels
}
}
// prints "HoverCar"
let drivableHoverCar = HoverCar(wheels: 4)
func makeNewCarFromCar<T: Car>(car: T) -> T {
return T(wheels: car.wheels)
}
// prints "Extension"
makeNewCarFromCar(drivableHoverCar)
So if you make a Car from a generic context where the type on which you call init is only to be known as Car the extension initializer is called even though an initializer is defined in HoverCar. This only occurs because there is no init(wheels: Int) requirement in the protocol.
If you add it you have the former problem with declaring the class as final but now it prints two times "HoverCar". Either way the second problem probably never occurs so it might be a better solution.
Sidenote: If I have made some mistakes (code, language, grammar,...) you're welcome to correct me :)
My understanding is that this isn't possible, because the protocol extension can't know which properties the conforming class or struct has - and therefore cannot guarantee they are correctly initialized.
If there are ways to get around this, I'm very interested to know! :)
#Qbyte is correct.
In addition, you can take a look at my Configurable
In that I have Initable protocol
public protocol Initable {
// To make init in protocol extension work
init()
}
public extension Initable {
public init(#noescape block: Self -> Void) {
self.init()
block(self)
}
}
Then in order to conform to it
extension Robot: Initable { }
I have 2 ways, using final or implement init
final class Robot {
var name: String?
var cute = false
}
class Robot {
var name: String?
var cute = false
required init() {
}
}
May not be the same but in my case instead of using init I used a static func to return the object of the class.
protocol Serializable {
static func object(fromJSON json:JSON) -> AnyObject?
}
class User {
let name:String
init(name:String) {
self.name = name
}
}
extension User:Serializable {
static func object(fromJSON json:JSON) -> AnyObject? {
guard let name = json["name"] else {
return nil
}
return User(name:name)
}
}
Then to create the object I do something like:
let user = User.object(fromJSON:json) as? User
I know its not the best thing ever but its the best solution I could find to not couple business model with the data layer.
NOTE: I'm lazy and I coded everything directly in the comment so if something doesn't work let me know.

Can I simulate traits/mixins in Swift?

Does Swift have a way of mixing in traits, a la Scala? The section of the Swift book on using extensions to add protocols to existing classes comes tantalizingly close. However, since protocols can't contain an implementation, this can't be used to mix code into a class. Is there another way?
As of Swift 2.0, yes!
Providing Default Implementations
You can use protocol extensions to provide a default implementation
to any method or property requirement of that protocol. If a
conforming type provides its own implementation of a required method
or property, that implementation will be used instead of the one
provided by the extension.
One way to simulate mixing is use generic function to provide implementation
For example with these protocols
protocol Named {
func GetName() -> String
}
protocol NamedExtension {
func GetLowercaseName() -> String
func GetUppercaseName() -> String
}
I want some class to implement GetName() and use mixing so they also get GetLowercaseName() and GetUppercaseName() without implement them
This is the implementation of NamedExtension as in free function
func GetLowercaseNameImpl<T:Named>(obj:T) -> String {
return obj.GetName().lowercaseString
}
func GetUppercaseNameImpl<T:Named>(obj:T) -> String {
return obj.GetName().uppercaseString
}
and extensions on Int
extension Int : Named {
func GetName() -> String {
return "Int"
}
}
extension Int : NamedExtension {
// use provided implementation
func GetLowercaseName() -> String {
return GetLowercaseNameImpl(self)
}
func GetUppercaseName() -> String {
return GetUppercaseNameImpl(self)
}
}
and I can use
1.GetName() // result Int
1.GetUppercaseName() // result "INT"
1.GetLowercaseName() // result "int"
I don't know Scala, but from what you're telling me it is possible to simultaneously create a protocol and an extension that extends a type to add "pseudo-trait" behavior.
For example:
protocol IsGreaterThan
{
func isGreaterThan(other:Int) -> Bool
func isNotGreaterThan(other:Int) -> Bool
}
extension Int : IsGreaterThan
{
func isGreaterThan(other:Int) -> Bool
{
return self > other
}
func isNotGreaterThan(other:Int) -> Bool
{
return !isGreaterThan(other)
}
}
The real hamstring is how generics are somewhat limited for now. I think they will improve a lot in the coming revisions of Swift.
Similar to Bryan Chen's answer:
import Foundation
protocol Named {
var name : String { get }
}
protocol NamedExtension : Named { // NB extends Named
var lowercaseName : String { get }
var uppercaseName : String { get }
}
struct NamedExtensionDefault { // Put defaults inside a struct to keep name spaces seperate
static func lowercaseName(named : NamedExtension) -> String {
return (named.name as NSString).lowercaseString
}
static func uppercaseName(named : NamedExtension) -> String {
return (named.name as NSString).uppercaseString
}
}
extension Int : NamedExtension {
var name : String {
return "Int"
}
// Use default implementation
var lowercaseName : String {
return NamedExtensionDefault.lowercaseName(self)
}
var uppercaseName : String {
return NamedExtensionDefault.uppercaseName(self)
}
}
1.name // result Int
1.uppercaseName // result "INT"
1.lowercaseName // result "int"
The main difference from Bryan's answer is that I didn't use generics because I made NamedExtension extends Named, so that the default implementations can access name.
Here's my (not yet widely tested) way of doing what I think are Scala traits in Swift 2.1.1, Playgrounds-ready, two versions:
Less flexible:
protocol BigBadProtocol {
func madFunc() -> String;
// func otherFunc();
// Maybe a couple more functions here.
}
protocol BlueMadFuncUser: BigBadProtocol {}
extension BlueMadFuncUser {
func madFunc() -> String {
return "Blue"
}
}
protocol RedMadFuncUser: BigBadProtocol {}
extension RedMadFuncUser {
func madFunc() -> String {
return "Red"
}
}
class ClearClass: BigBadProtocol {
func madFunc() -> String {
return "Clear"
}
}
class BlueClass: BlueMadFuncUser {}
class RedClass: RedMadFuncUser {}
More flexible:
protocol BigBadProtocol {
func madFunc() -> String;
// func otherFunc();
// Maybe a couple more functions here.
}
protocol BlueMadFuncUser {}
extension BigBadProtocol where Self: BlueMadFuncUser {
func madFunc() -> String {
return "Blue"
}
}
protocol RedMadFuncUser {}
extension BigBadProtocol where Self: RedMadFuncUser {
func madFunc() -> String {
return "Red"
}
}
class ClearClass: BigBadProtocol {
func madFunc() -> String {
return "Clear"
}
}
class BlueClass: BigBadProtocol, BlueMadFuncUser {}
class RedClass: BigBadProtocol, RedMadFuncUser {}
Sanity check:
var classes: [BigBadProtocol] = [ClearClass(), BlueClass(), RedClass()]
// Prints "Clear, Blue, Red\n"
print((classes.map { $0.madFunc() }).joinWithSeparator(", "))
// Print another way for Playgrounds, which appears to bug out on the lines above
var s = ""
for klass in classes {
s += klass.madFunc() + " "
}
print(s)
BlueMadFuncUser and RedMadFuncUser are two versions of a trait. My terminology might be off, but then you can independently create a second trait like that and mix and match in your classes as you please.
Would be much more challenging or boiler-plate-y to reuse logic like that with an inheritance-based approach.
I ended up wanting this pattern after finding it very useful in Hack for PHP, where from what I can tell traits are very similar to Scala's: https://docs.hhvm.com/hack/other-features/trait-and-interface-requirements)