The Swift developers seem to be saying that polymorphism should be accomplished via protocols, not class inheritance. For example, let's say you have an array of Shapes and the Shapes have different draw methods, Shape should be a protocol not a superclass.
protocol Shape {
func draw()
}
class Circle: Shape {
func draw() { print("Drawing a circle") }
}
class Triangle: Shape {
func draw() { print("Drawing a triangle") }
}
var shapes = [Shape]()
func fillShapes() {
//... Add circles and triangles to shapes ...
}
// run
fillShapes()
for shape in shapes {
shape.draw()
}
Now say you want to build Shapes based on user input which produces Strings. How would you write a Shape constructor that accepts String as input, in the most Swift-y way?
Cristik in this question suggests a global function like this:
func buildShape(kind: String) -> Shape? {
switch kind {
case "Circle": return Circle()
case "Triangle": return Triangle()
default: return nil // Error - bad Shape
}
}
This seems messy to me. I would rather the constructor was incorporated into Shape.
So my first attempt was
extension Shape {
static func build(kind: String) -> Shape? {
switch kind {
case "Circle": return Circle()
case "Triangle": return Triangle()
default: return nil // Error - bad Shape
}
}
}
But calling Shape.build("Circle")! produces error: static member 'build' cannot be used on instance of type 'Shape.Protocol'
I also tried
extension Shape {
class Factory {
func build(string: String) -> Shape? {
switch kind {
case "Circle": return Circle()
case "Triangle": return Triangle()
default: return nil
}
}
}
static let factory = Factory()
}
shapes.append(Shape.factory.build("Circle")!)
But it says Factory cannot be defined inside a protocol extension.
How does Swift want us to make flexible constructors?
You are overthinking the problem. There is nothing cleaner than a "global" function.
The problem with global functions in some languages is that they are polluting the global namespace which leads to name conflicts.
However, every Swift project has its own namespace and every framework it imports has also its own namespace, so there is no problem with name conflicts, thus, there is nothing wrong with global functions.
There is no need to wrap simple functions into Factory classes just to create an illusion of a namespace (this would be the correct solution in Java because you cannot create a global function in Java).
Function accessed as Shape.Factory.build(...) is not any better than a function accessed using buildShape(...).
If you really want to have a namespace, you can just wrap your functions into a struct:
struct Shapes {
static func buildShape(kind: String) -> Shape? {
switch kind {
case "Circle": return Circle()
case "Triangle": return Triangle()
default: return nil // Error - bad Shape
}
}
}
and call it as Shapes.buildShape(...) but there is not real need for it.
The protocol cannot (and in my personal opinion, should not) be used as a "factory".
A protocol is a set of rules that a construct follows, not a concrete construct itself. A factory should be a concrete type, not a "set of rules". The solution is to have a separate ShapeFactory construct (be it a class, struct or enum), with static/non-static build functions.
As for the "messy" part; I agree. It is kind of messy. Swift doesn't isn't capable of things like struct Shape.Factory { }. There's no real way to get around it without making a compromise / rewriting the compiler, the latter of which can actually be achieved with some good constructive criticism at the swift-evolution mailing list.
I wouldn't do for a Factory method on Shape, neither a protocol nor a super class should know details about implementations or derived classes. I'd rather go for a ShapeBuilding protocol and a factory class.
protocol Shape {
func draw()
}
protocol ShapeBuilding {
static func build(kind:String) -> Shape?
}
struct ShapeBuilder: ShapeBuilding {
static func build(kind: String) -> Shape? {
switch kind {
case "Circle": return Circle()
case "Triangle": return Triangle()
default: return nil // Error - bad Shape
}
}
}
let circle = ShapeBuilder.build("Circle")
Motto: Just because Swift offers extensions we don't have to force everything into extension.
All three of you have given me good insight into this question. I now see that it is a much deeper question than I first imagined.
Here is my consolidated answer.
The Swift message is Avoid Building Abstract Class Hierarchy. Protocols are glue (or grease maybe?) not abstract superclasses.
In Swift, a protocol like Shape is not an abstract superclass of Circle and Triangle in the Java sense. Don't try to put factory constructors (constructors that can return more than one type) into protocols.
The previous version of this post suggested that shapes could hold the differentiated construction code. But I have changed my mind. shapes shouldn't know about circles and triangles either. shapes is polymorphic and that's what polymorphic means. Therefore I have substantially changed this post. If there was strike-through I would use it but I don't see it.
I now think a global function like this is the right way.
func buildShapeFrom(userInput: String) -> Shape? {
switch userInput {
case "Circle": return Circle()
case "Triangle": return Triangle()
default: return nil // Error - bad input
}
}
If there's an appropriate structure on the user input side, perhaps it could go there. The user cares whether it is a circle or a triangle, but Shape shouldn't and neither should shapes.
Comments welcome.
Related
Situation
I have a two generic classes which will fetch data either from api and database, lets say APIDataSource<I, O> and DBDataSource<I, O> respectively
I will inject any of two class in view-model when creating it and view-model will use that class to fetch data it needed. I want view-model to work exactly same with both class. So I don't want different generic constraints for the classes
// sudo code
ViewModel(APIDataSource <InputModel, ResponseModel>(...))
// I want to change the datasource in future like
ViewModel(DBDataSource <InputModel, ResponseModel>(...))
To fetch data from api ResponseModel need to confirms to "Decodable" because I want to create that object from JSON. To fetch data from realm database it need to inherit from Object
Inside ViewModel I want to get response like
// sudo code
self.dataSource.request("param1", "param2")
If developer tries to fetch api data from database or vice-versa it will check for correct type and throws proper error.
Stripped out version of code for playground
Following is stripped out version of code which shows what I want to achieve or where I am stuck (casting un-constrained generic type to generic type that confirms to Decodable)
import Foundation
// Just to test functions below
class DummyModel: Decodable {
}
// Stripped out version of function which will convert json to object of type T
func decode<T:Decodable>(_ type: T.Type){
print(type)
}
// This doesn't give compilation error
// Ignore the inp
func testDecode<T:Decodable> (_ inp: T) {
decode(T.self)
}
// This gives compilation error
// Ignore the inp
func testDecode2<T>(_ inp: T){
if(T.self is Decodable){
// ??????????
// How can we cast T at runtime after checking T confirms to Decodable??
decode(T.self as! Decodable.Type)
}
}
testDecode(DummyModel())
Any help or explanation that this could not work would be appreciated. Thanks in advance :)
As #matt suggests, moving my various comments over to an answer in the form "your problem has no good solution and you need to redesign your problem."
What you're trying to do is at best fragile, and at worst impossible. Matt's approach is a good solution when you're trying to improve performance, but it breaks in surprising ways if it impacts behavior. For example:
protocol P {}
func doSomething<T>(x: T) -> String {
if x is P {
return "\(x) simple, but it's really P"
}
return "\(x) simple"
}
func doSomething<T: P>(x: T) -> String {
return "\(x) is P"
}
struct S: P {}
doSomething(x: S()) // S() is P
So that works just like we expect. But we can lose the type information this way:
func wrapper<T>(x: T) -> String {
return doSomething(x: x)
}
wrapper(x: S()) // S() simple, but it's really P!
So you can't solve this with generics.
Going back to your approach, which at least has the possibility of being robust, it's still not going to work. Swift's type system just doesn't have a way to express what you're trying to say. But I don't think you should be trying to say this anyway.
In the method that fetch data I will check type of generic type and if it confirms to "Decodable" protocol I will use it to fetch data from api else from database.
If fetching from the API vs the database represents different semantics (rather than just a performance improvement), this is very dangerous even if you could get it to work. Any part of the program can attach Decodable to any type. It can even be done in a separate module. Adding protocol conformance should never change the semantics (outwardly visible behaviors) of the program, only the performance or capabilities.
I have a generic class which will fetch data either from api or database
Perfect. If you already have a class, class inheritance makes a lot of sense here. I might build it like:
class Model {
required init(identifier: String) {}
}
class DatabaseModel {
required init(fromDatabaseWithIdentifier: String) {}
convenience init(identifier: String) { self.init(fromDatabaseWithIdentifier: identifier )}
}
class APIModel {
required init(fromAPIWithIdentifier: String) {}
convenience init(identifier: String) { self.init(fromAPIWithIdentifier: identifier )}
}
class SomeModel: DatabaseModel {
required init(fromDatabaseWithIdentifier identifier: String) {
super.init(fromDatabaseWithIdentifier: identifier)
}
}
Depending on your exact needs, you might rearrange this (and a protocol might also be workable here). But the key point is that the model knows how to fetch itself. That makes it easy to use Decodable inside the class (since it can easily use type(of: self) as the parameter).
Your needs may be different, and if you'll describe them a bit better maybe we'll come to a better solution. But it should not be based on whether something merely conforms to a protocol. In most cases that will be impossible, and if you get it working it will be fragile.
What you'd really like to do here is have two versions of testDecode, one for when T conforms to Decodable, the other for when it doesn't. You would thus overload the function testDecode so that the right one is called depending on the type of T.
Unfortunately, you can't do that, because you can't do a function overload that depends on the resolution of a generic type. But you can work around this by boxing the function inside a generic type, because you can extend the type conditionally.
Thus, just to show the architecture:
protocol P{}
struct Box<T> {
func f() {
print("it doesn't conform to P")
}
}
extension Box where T : P {
func f() {
print("it conforms to P")
}
}
struct S1:P {}
struct S2 {}
let b1 = Box<S1>()
b1.f() // "it conforms to P"
let b2 = Box<S2>()
b2.f() // "it doesn't conform to P"
This proves that the right version of f is being called, depending on whether the type that resolves the generic conforms to the protocol or not.
I have code that follows the general design of:
protocol DispatchType {}
class DispatchType1: DispatchType {}
class DispatchType2: DispatchType {}
func doBar<D:DispatchType>(value:D) {
print("general function called")
}
func doBar(value:DispatchType1) {
print("DispatchType1 called")
}
func doBar(value:DispatchType2) {
print("DispatchType2 called")
}
where in reality DispatchType is actually a backend storage. The doBarfunctions are optimized methods that depend on the correct storage type. Everything works fine if I do:
let d1 = DispatchType1()
let d2 = DispatchType2()
doBar(value: d1) // "DispatchType1 called"
doBar(value: d2) // "DispatchType2 called"
However, if I make a function that calls doBar:
func test<D:DispatchType>(value:D) {
doBar(value: value)
}
and I try a similar calling pattern, I get:
test(value: d1) // "general function called"
test(value: d2) // "general function called"
This seems like something that Swift should be able to handle since it should be able to determine at compile time the type constraints. Just as a quick test, I also tried writing doBar as:
func doBar<D:DispatchType>(value:D) where D:DispatchType1 {
print("DispatchType1 called")
}
func doBar<D:DispatchType>(value:D) where D:DispatchType2 {
print("DispatchType2 called")
}
but get the same results.
Any ideas if this is correct Swift behavior, and if so, a good way to get around this behavior?
Edit 1: Example of why I was trying to avoid using protocols. Suppose I have the code (greatly simplified from my actual code):
protocol Storage {
// ...
}
class Tensor<S:Storage> {
// ...
}
For the Tensor class I have a base set of operations that can be performed on the Tensors. However, the operations themselves will change their behavior based on the storage. Currently I accomplish this with:
func dot<S:Storage>(_ lhs:Tensor<S>, _ rhs:Tensor<S>) -> Tensor<S> { ... }
While I can put these in the Tensor class and use extensions:
extension Tensor where S:CBlasStorage {
func dot(_ tensor:Tensor<S>) -> Tensor<S> {
// ...
}
}
this has a few side effects which I don't like:
I think dot(lhs, rhs) is preferable to lhs.dot(rhs). Convenience functions can be written to get around this, but that will create a huge explosion of code.
This will cause the Tensor class to become monolithic. I really prefer having it contain the minimal amount of code necessary and expand its functionality by auxiliary functions.
Related to (2), this means that anyone who wants to add new functionality will have to touch the base class, which I consider bad design.
Edit 2: One alternative is that things work expected if you use constraints for everything:
func test<D:DispatchType>(value:D) where D:DispatchType1 {
doBar(value: value)
}
func test<D:DispatchType>(value:D) where D:DispatchType2 {
doBar(value: value)
}
will cause the correct doBar to be called. This also isn't ideal, as it will cause a lot of extra code to be written, but at least lets me keep my current design.
Edit 3: I came across documentation showing the use of static keyword with generics. This helps at least with point (1):
class Tensor<S:Storage> {
// ...
static func cos(_ tensor:Tensor<S>) -> Tensor<S> {
// ...
}
}
allows you to write:
let result = Tensor.cos(value)
and it supports operator overloading:
let result = value1 + value2
it does have the added verbosity of required Tensor. This can made a little better with:
typealias T<S:Storage> = Tensor<S>
This is indeed correct behaviour as overload resolution takes place at compile time (it would be a pretty expensive operation to take place at runtime). Therefore from within test(value:), the only thing the compiler knows about value is that it's of some type that conforms to DispatchType – thus the only overload it can dispatch to is func doBar<D : DispatchType>(value: D).
Things would be different if generic functions were always specialised by the compiler, because then a specialised implementation of test(value:) would know the concrete type of value and thus be able to pick the appropriate overload. However, specialisation of generic functions is currently only an optimisation (as without inlining, it can add significant bloat to your code), so this doesn't change the observed behaviour.
One solution in order to allow for polymorphism is to leverage the protocol witness table (see this great WWDC talk on them) by adding doBar() as a protocol requirement, and implementing the specialised implementations of it in the respective classes that conform to the protocol, with the general implementation being a part of the protocol extension.
This will allow for the dynamic dispatch of doBar(), thus allowing it to be called from test(value:) and having the correct implementation called.
protocol DispatchType {
func doBar()
}
extension DispatchType {
func doBar() {
print("general function called")
}
}
class DispatchType1: DispatchType {
func doBar() {
print("DispatchType1 called")
}
}
class DispatchType2: DispatchType {
func doBar() {
print("DispatchType2 called")
}
}
func test<D : DispatchType>(value: D) {
value.doBar()
}
let d1 = DispatchType1()
let d2 = DispatchType2()
test(value: d1) // "DispatchType1 called"
test(value: d2) // "DispatchType2 called"
I'm trying to push MVVM patterns on my application, but I've found that quite difficult using Swift and Generics. Example:
I have a Comic struct on my model layer. But I want my viewControllers to consume objects conforming ComicViewModel protocol in order to increase separation of concerns.
I'm wrapping the model structs in a class called Box defined like this:
public class Box<T> {
public let value: T
public init(_ value: T) { self.value = value }
}
This is used only to wrap the actual return value in a Result enum like the one explained here
However, the Swift compiler doesn't seem to like the cast from Comic to ComicViewModel even if I'm clearly defining that
func sampleComics() -> Box<Comic> {...}
protocol ComicViewModel {...}
extension Comic : ComicViewModel {...}
func fetchComicsViewModel() -> Box<ComicViewModel> {
return sampleComics() //ERROR
}
Full playground available here.
Thanks a lot for your help!
Sad to say, Swift doesn't perform implicit casting like that. As of now, you have to re-Box() the value.
func fetchComicsViewModel() -> Box<ComicViewModel> {
return Box(sampleComics().value)
}
Building on previous question which got resolved, but it led to another problem. If protocol/class types are stored in a collection, retrieving and instantiating them back throws an error. a hypothetical example is below. The paradigm is based on "Program to Interface not an implementation" What does it mean to "program to an interface"?
instantiate from protocol.Type reference dynamically at runtime
public protocol ISpeakable {
init()
func speak()
}
class Cat : ISpeakable {
required init() {}
func speak() {
println("Meow");
}
}
class Dog : ISpeakable {
required init() {}
func speak() {
println("Woof");
}
}
//Test class is not aware of the specific implementations of ISpeakable at compile time
class Test {
func instantiateAndCallSpeak<T: ISpeakable>(Animal:T.Type) {
let animal = Animal()
animal.speak()
}
}
// Users of the Test class are aware of the specific implementations at compile/runtime
//works
let t = Test()
t.instantiateAndCallSpeak(Cat.self)
t.instantiateAndCallSpeak(Dog.self)
//doesn't work if types are retrieved from a collection
//Uncomment to show Error - IAnimal.Type is not convertible to T.Type
var animals: [ISpeakable.Type] = [Cat.self, Dog.self, Cat.self]
for animal in animals {
//t.instantiateAndCallSpeak(animal) //throws error
}
for (index:Int, value:ISpeakable.Type) in enumerate(animals) {
//t.instantiateAndCallSpeak(value) //throws error
}
Edit - My current workaround to iterate through collection but of course it's limiting as the api has to know all sorts of implementations. The other limitation is subclasses of these types (for instance PersianCat, GermanShepherd) will not have their overridden functions called or I go to Objective-C for rescue (NSClassFromString etc.) or wait for SWIFT to support this feature.
Note (background): these types are pushed into array by users of the utility and for loop is executed on notification
var animals: [ISpeakable.Type] = [Cat.self, Dog.self, Cat.self]
for Animal in animals {
if Animal is Cat.Type {
if let AnimalClass = Animal as? Cat.Type {
var instance = AnimalClass()
instance.speak()
}
} else if Animal is Dog.Type {
if let AnimalClass = Animal as? Dog.Type {
var instance = AnimalClass()
instance.speak()
}
}
}
Basically the answer is: correct, you can't do that. Swift needs to determine the concrete types of type parameters at compile time, not at runtime. This comes up in a lot of little corner cases. For instance, you can't construct a generic closure and store it in a variable without type-specifying it.
This can be a little clearer if we boil it down to a minimal test case
protocol Creatable { init() }
struct Object : Creatable { init() {} }
func instantiate<T: Creatable>(Thing: T.Type) -> T {
return Thing()
}
// works. object is of type "Object"
let object = instantiate(Object.self) // (1)
// 'Creatable.Type' is not convertible to 'T.Type'
let type: Creatable.Type = Object.self
let thing = instantiate(type) // (2)
At line 1, the compiler has a question: what type should T be in this instance of instantiate? And that's easy, it should be Object. That's a concrete type, so everything is fine.
At line 2, there's no concrete type that Swift can make T. All it has is Creatable, which is an abstract type (we know by code inspection the actual value of type, but Swift doesn't consider the value, just the type). It's ok to take and return protocols, but it's not ok to make them into type parameters. It's just not legal Swift today.
This is hinted at in the Swift Programming Language: Generic Parameters and Arguments:
When you declare a generic type, function, or initializer, you specify the type parameters that the generic type, function, or initializer can work with. These type parameters act as placeholders that are replaced by actual concrete type arguments when an instance of a generic type is created or a generic function or initializer is called. (emphasis mine)
You'll need to do whatever you're trying to do another way in Swift.
As a fun bonus, try explicitly asking for the impossible:
let thing = instantiate(Creatable.self)
And... swift crashes.
From your further comments, I think closures do exactly what you're looking for. You've made your protocol require trivial construction (init()), but that's an unnecessary restriction. You just need the caller to tell the function how to construct the object. That's easy with a closure, and there is no need for type parameterization at all this way. This isn't a work-around; I believe this is the better way to implement that pattern you're describing. Consider the following (some minor changes to make the example more Swift-like):
// Removed init(). There's no need for it to be trivially creatable.
// Cocoa protocols that indicate a method generally end in "ing"
// (NSCopying, NSCoding, NSLocking). They do not include "I"
public protocol Speaking {
func speak()
}
// Converted these to structs since that's all that's required for
// this example, but it works as well for classes.
struct Cat : Speaking {
func speak() {
println("Meow");
}
}
struct Dog : Speaking {
func speak() {
println("Woof");
}
}
// Demonstrating a more complex object that is easy with closures,
// but hard with your original protocol
struct Person: Speaking {
let name: String
func speak() {
println("My name is \(name)")
}
}
// Removed Test class. There was no need for it in the example,
// but it works fine if you add it.
// You pass a closure that returns a Speaking. We don't care *how* it does
// that. It doesn't have to be by construction. It could return an existing one.
func instantiateAndCallSpeak(builder: () -> Speaking) {
let animal = builder()
animal.speak()
}
// Can call with an immediate form.
// Note that Cat and Dog are not created here. They are not created until builder()
// is called above. #autoclosure would avoid the braces, but I typically avoid it.
instantiateAndCallSpeak { Cat() }
instantiateAndCallSpeak { Dog() }
// Can put them in an array, though we do have to specify the type here. You could
// create a "typealias SpeakingBuilder = () -> Speaking" if that came up a lot.
// Again note that no Speaking objects are created here. These are closures that
// will generate objects when applied.
// Notice how easy it is to pass parameters here? These don't all have to have the
// same initializers.
let animalBuilders: [() -> Speaking] = [{ Cat() } , { Dog() }, { Person(name: "Rob") }]
for animal in animalBuilders {
instantiateAndCallSpeak(animal)
}
I have a type XY that has various fields and methods (a few dozen).
type XY struct {
Name string
SomeValue int
...
}
func (xy *XY) Do1() { ... }
func (xy *XY) Do2() { ... }
func (xy *XY) Do3() { ... }
...
Now I want to define a second type that embeds XY, keeping all fields and methods. But I do want to modify a few functions.
type AB struct {
XY
}
func (ab *AB) Do2() { ... }
So far so good. Now I want to pass AB to a function that takes XY.
func SomeFunc(xy *XY) { ... }
And here's where I stumble, no polymorphism.
I can pass *AB.XY to the function, but that wouldn't make use of AB's Do2 function anymore. I could make an interface for it, which is probably the intended way, but if SomeFunc were to need near to all functions of XY, say getters of almost all fields, I'd basically need to create a copy of XY, as an interface (possible use case: I'm on a server and have to send values to a client in a specific way). I wouldn't want to only make it an interface, because I'd have to redefine all fields and functions in all types that are using the interface.
I can think of solutions for this problem, e.g. making Do2 a closure, that is set depending on the "host" type, or an enum field in XY and changing the behavior of Do2 based on that "type" variable, or using reflection and interface{} in SomeFunc, but I'd like to know what the "correct" way in Go would be. How do you do this in the most efficient way, even if you have a lot of functions?
The correct way to do it in Go is to use interfaces. Create an interface of Do1, Do2, etc. and make SomeFunc work with the interface type.
Like #Ainar-G said, using an interface is the proper way for that kind of behavior, for example:
type Doer interface {
Do1()
Do2()
}
func (*S1) Do1() {
println("S1.Do1")
}
func (*S1) Do2() {
println("S1.Do2")
}
type S2 struct{ S1 }
func (*S2) Do1() {
println("S2.Do1")
}
func DoIt(d Doer) {
d.Do1()
d.Do2()
// you can use a type switch for specific corner cases, or implement a MarshalJson (or similar) interface for you types)
switch v := d.(type) {
case *S1:
println("Special case for S1", v)
case *S2:
println("Special case for S2", v)
}
}
playground