Difference between using Generic and Protocol as type parameters, what are the pros and cons of implement them in a function - swift

Since Swift allows us using both Protocol and Generic as parameter types in a function, the scenario below has come into my mind:
protocol AProtocol {
var name: String{ get }
}
class ClassA: AProtocol {
var name = "Allen"
}
func printNameGeneric<T: AProtocol>(param: T) {
print(param.name)
}
func printNameProtocol(param: AProtocol) {
print(param.name)
}
The first function uses generic as parameter type with a type constraint, and the second function uses protocol as the parameter type directly. However, these two functions can have the same effect, which is the point confusing me. So my questions are:
What are the specific scenarios for each of them (or a case which can only be done by the specific one, but not another)?
For the given case, both functions turn out the same result. Which one is better to implement (or the pros and cons of each of them)?
This great talk has mentioned generic specialization, which is a optimization that turn the way of function dispatching from dynamic dispatching (function with non-generic parameters) to static dispatching or inlining (function with generic parameters). Since static dispatching and inlining are less expensive in contrast with dynamic dispatching, to implement functions with generic can always provide a better performance.
#Hamish also gave great information in this post, have a look for more information.
Here is a new question came to me:
struct StructA: AProtocol {
var a: Int
}
struct StructB: AProtocol {
var b: Int
}
func buttonClicked(sender: UIButton) {
var aVar: AProtocol
if sender == self.buttonA
{
aVar = StructA(a: 1)
}
else if sender == self.buttonA
{
aVar = StructB(b: 2)
}
foo(param: aVar)
}
func foo<T: AProtocol>(param: T) {
//do something
}
If there are several types conform to a Protocol, and are pass in to a generic function in different conditions dynamically. As shown above, pressing different buttons will pass different types(StructA or StructB) of parameter into function, would the generic specialization still work in this case?

There is actually a video from this year's WWDC about that (it was about performance of classes, structs and protocols; I don't have a link but you should be able to find it).
In your second function, where you pass a any value that conforms to that protocol, you are actually passing a container that has 24 bytes of storage for the passed value, and 16 bytes for type related information (to determine which methods to call, ergo dynamic dispatch). If the passed value is now bigger than 24 bytes in memory, the object will be allocated on the heap and the container stores a reference to that object! That is actually extremely time consuming and should certainly be avoided if possible.
In your first function, where you use a generic constraint, there is actually created another function by the compiler that explicitly performs the function's operations upon that type. (If you use this function with lots of different types, your code size may, however, increase significantly; see C++ code bloat for further reference.) However, the compiler can now statically dispatch the methods, inline the function if possible and does certainly not have to allocate any heap space. Stated in the video mentioned above, code size does not have to increase significantly as code can still be shared... so the function with generic constraint is certainly the way to go!

Now we have two protocol below:
protocol A {
func sometingA()
}
protocol B {
func sometingB()
}
Then the parameter need to conform to both A and B.
//Generic solution
func methodGeneric<T:A>(t:T)where T:B {
t.sometingA()
t.sometingB()
}
//we need protocol C to define the parameter type
protocol C:A,B {}
//Protocol solution
func methodProtocol(c:C){
c.sometingA()
c.sometingB()
}
It seems that nothing is wrong but when we define a struct like this:
struct S:A,B {
func sometingB() {
print("B")
}
func sometingA() {
print("A")
}
}
The methodGeneric works but we need to change struct S:A,B to struct S:C to make methodProtocol work. Some questions:
Do we really need protocol C?
Why not would we write like func method(s:S)?
You could read more about this in the Generic Doc for additional information .

Related

Why does the protocol default value passed to the function not change, even though the function does when subclassing?

I have a protocol, to which I have assigned some default values:
protocol HigherProtocol {
var level: Int { get }
func doSomething()
}
extension HigherProtocol {
var level: Int { 10 }
func doSomething() {
print("Higher level is \(level)")
}
}
Then I have another protocol which conforms to the higher level protocol, but has different default values and implementation of functions:
protocol LowerProtocol: HigherProtocol {}
extension LowerProtocol {
var level: Int { 1 }
func doSomething() {
print("Lower level is \(level)")
}
}
I then create a class that conforms to the HigherProtocol, and then a subclass that conforms to the lower level protocol:
class HigherClass: HigherProtocol {}
class LowerClass: HigherClass, LowerProtocol {}
When I instantiate this lower class, however, it displays some odd behaviour:
let lowerClass = LowerClass()
lowerClass.level // is 1
lowerClass.doSomething() // Prints "Lower level is 10" to the console.
The default property is correct, but the default implementation of the function seems to be a hybrid of the two.
I'm wondering what's happening here?
You appear to be trying to use protocols to create multiple-inheritance. They're not designed for that, and even if you get this working, you're going to get bitten several times. Protocols are not a replacement for inheritance, multiple or otherwise. (As a rule, Swift favors composition rather than inheritance in any form.)
The problem here is that HigherClass conforms to HigherProtocol and so now has implementations for level and doSomething. LowerClass inherits from that, and wants to override those implementations. But the overrides are in a protocol extension, which is undefined behavior. See Extensions from The Swift Programming Language:
Extensions can add new functionality to a type, but they cannot override existing functionality.
Undefined behavior doesn't mean "it doesn't override." It means "anything could happen" including this weird case where it sometimes is overridden and sometimes isn't.
(As a side note, the situation is similar in Objective-C. Implementing a method in two different categories makes it undefined which one is called, and there's no warning or error to let you when this happens. Swift's optimizations can make the behavior even more surprising.)
I wish the compiler could detect these kinds of mistakes and raise an error, but it doesn't. You'll need to redesign your system to not do this.
Protocols are existential types that is why you are confused. You need to expose to protocol types of your class Type. In your case you can do LowerProtocol or HigherProtocol so it prints 10 now. Let`s make like this
let lowerClass: LowerProtocol = LowerClass()
or
let lowerClass: HigherProtocol = LowerClass()
lowerClass.level // now prints 10
lowerClass.doSomething() // Prints "Lower level is 10" to the console.

Swift 5: how to specify a generic type conforming to protocol when declaring a variable

I am on Swift 5. I have a protocol:
protocol Pipe {
associatedtype T
func await() -> Void
func yield( to: Any, with listener: Selector ) -> Void
}
And I would like to reference an instance of this protocol somewhere in code. That is, I want foo : , or a variable of generic type T implementing Pipe. Per this documentation: https://docs.swift.org/swift-book/ReferenceManual/GenericParametersAndArguments.html
I tried writing:
var imageSource: <Pipe T>
and any permutation of said symbols, ie imageSource: but the syntax is wrong across all cases.
In fact, T conform to two protocols, Renderable and Pipe, so I really want:
var imageSource: <Pipe, Renderable T>
Syntax wise this is gibberish, but semantically it's not an uncommon use case.
__________________ EDIT after two answers have been given __________
I tried simplifying the Pipe protocol for this post, but now I realize I simplified it too much. In my code base it's
protocol Pipe {
associatedtype T
func await() -> Void
func yield( to: Any, with listener: Selector ) -> Void
func batch() -> [T]
}
That's why there's a T there. But it's not crucial, I can drop the batch() -> [T] if I am able to write what I want above.
An associated type is used when you want your protocol to work with a variety of types, think a Container protocol that might have several methods all dealing with one contained type.
But your protocol is not that, it doesn't need to know any other types to specify the necessary behavior, so get rid of the associated type.
protocol Pipe {
func await() -> Void
func yield( to: Any, with listener: Selector ) -> Void
}
class Foo {
var imageSource: Pipe & Renderable
}
This is called a generalized existential, and is not available in Swift. A protocol with an associated type describes other types; it is not a type itself and cannot be the type of a variable, or put into a collection.
This specific protocol doesn't make a lot of sense, since you don't use T anywhere. But what you would need to do is pull this into the containing type:
struct Something<Source> where Source: Pipe & Renderable {
var imageSource: Source
}
I suspect you really want to redesign this in a different way, however. This looks like a fairly common misuse of protocols. You probably want Pipe and Renderer types that are structs (or even just functions). Without knowing what the calling code looks like, I can't say precisely how you would design it.
If you remove T (which isn't being used here), then Max's answer will address this issue. Protocols without associated types have an implicit existential type, and so you can treat them somewhat as "normal" types (assigning them to variables or putting them in collections).

Strange behavior with Swift generics and protocol extensions

I'm seeing some strange behavior at the interface of protocol extensions and generics. I'm new to Swift, so possibly I misunderstand, but I don't see how this can be correct behavior.
First let's define a protocol, and extend it with a default function implementation:
protocol Foo {
}
extension Foo {
static func yo() {
print("Foo.yo")
}
}
Now define a couple of conforming types:
struct A: Foo {
}
struct B: Foo {
static func yo() {
print("B.yo")
}
}
A.yo()
B.yo()
As expected, A.yo() uses the default implementation of yo, whereas B.yo() uses the explicit implementation provided by B: the output is
Foo.yo
B.yo
Now let's make a simple generic type:
struct C<T: Foo> {
static func what() {
T.yo()
}
}
C<A>.what()
C<B>.what()
C<A>.what() prints Foo.yo, as expected. But C<B>.what() also prints Foo.yo!
Surely the meaning of C<B> is simply the template for C with B substituted in for the type parameter T? Yet B's version of yo is not being called.
What am I missing? I'm using Swift 5.2.2.
Now, as it turns out you can fix this problem by declaring yo in the original definition of Foo. If we do this:
protocol Foo {
static func yo()
}
then C<B>.what() works as I would expect, printing B.yo. I can't understand the original behavior in the first place, but even less can I understand how this would change it.
In my actual application I can't use this fix, because I am extending a pre-existing protocol with a function that I want to specialize in a particular conforming type.
Generics are resolved at compile time. They're not dynamically dispatched like method calls on class hierarchies or protocols. That staticness is kind of their point, that's where the performance wins stem from.
As far as I can tell, Foo.yo() and B.yo() are totally unrelated functions. Calling Foo.yo() does a statically dispatched call to Foo, and likewise, calling B.yo() causes a statically dispatched call to B.
Yet, if you up-cast B.self to a Foo.Type, and you call yo() on it, you end up with a statically dispatched call to Foo:
(B.self as Foo.Type).yo()
To get dynamic dispatch (to achieve the kind of polymorphism you're after), you need to define yo as a requirement of the protocol. That establishes a relationship between B.yo() (which is now a part of the conformance to the protocol) and Foo.yo() (which is a default implementation for types who don't provide their own).
protocol Foo {
// static func yo() // uncomment this
}
extension Foo {
static func yo() {
print("Foo.yo")
}
}
struct A: Foo {
}
struct B: Foo {
static func yo() {
print("B.yo")
}
}
struct C<T: Foo> {
static func what() {
T.yo()
}
}
A.yo()
B.yo()
(B.self as Foo.Type).yo()
C<A>.what()
C<B>.what()
Results before:
Foo.yo
B.yo
Foo.yo
Foo.yo
Foo.yo
Results after making yo a requirement:
Foo.yo
B.yo
B.yo
Foo.yo
B.yo
It’s hard to suggest a fix for your exact situation without more details of the exact situation- are you not able to provide these? Suffice to say this is the expected behaviour and its to do with some optimisations and assumptions the compiler makes.
You might want to check out this article on static vs dynamic dispatch in Swift: https://medium.com/#PavloShadov/https-medium-com-pavloshadov-swift-protocols-magic-of-dynamic-static-methods-dispatches-dfe0e0c85509

Swift: casting un-constrained generic type to generic type that confirms to Decodable

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.

Parameterizing closures as generic based on parameter-list/return type

Is there a way to make a generic type signature for a closure such that I can later call it generically? In particular my question is how to deal with an unknown number of arguments.
I have an object that I’d like to call a series of closures on an update, and I’d like other objects to be able to register closures that they’d like to be called with that first object.
Closures aren’t hashable, but I’d like to be able to also unregister a closure, so I wound up creating a custom type to handle this based on a dictionary:
//T is the block signature such as Double->()
struct ClosureCollection<T> : SequenceType {
private var idx=0
var closureDict:[Int:(T,NSOperationQueue)]=[:]
mutating func addClosure(b:T) -> Int {
return addClosure(NSOperationQueue.mainQueue(),b)
}
mutating func addClosure(q:NSOperationQueue, _ b:T) -> Int {
closureDict[idx]=(b,q)
idx+=1
return idx-1
}
mutating func dropClosure(k:Int) {
closureDict.removeValueForKey(k)
}
func generate() -> AnyGenerator<(T,NSOperationQueue)> {
var dgen=closureDict.generate()
return AnyGenerator {
return dgen.next()?.1
}
}
}
This lets me use the collection in code like this:
Declare it:
private var distributionPoints=ClosureCollection<(CMDeviceMotion?,NSError?) -> ()> ()
Use it:
for (p,q) in distributionPoints {
q.addOperationWithBlock {p(dm,error)}
}
So far, all’s good but it requires the caller to follow a pretty specific pattern to use the collection that I'd like to hide. I’d like to extend this idea to where I can ask the collection to run the for loop itself using syntax something like
distributionPoints.runAllWith(dm,error)
The problem is with the runAllWith signature-- my current implementation is generic over the full closure signature because I don't know how to make the number of arguments to the closure generic. I suspect this can be done if I know the blocks accept one argument and return one argument, for example, by using two type placeholders, say T for the argument and U for the return value.
What I don’t seem able to do is make this generic across an unknown number of parameters. The type I’m creating doesn’t care what the structure of the block is, it just wants to accept closures with a certain signature and then provide a mechanism to call them by exposing an API that depends on the closure signature.
I don’t think there’s support for variadic generic parameters, so I can’t go down that road. Tuple-splat was deprecated.
Is there a way to do this without requiring the caller to bundle arguments into a tuple and then requiring a wrapper around each closure to unwrap a tuple of arguments and do a hand-crafted tuple splat?