Swift generic T.Type becomes T.Protocol - swift

Swift 5.1
I'm writing a class that has a generic parameter T, and one of its methods accepts a type as an argument, where the type extends from T.Type. See foo1 below:
public protocol P {
}
public class C: P {
}
public class X<T> {
public func foo1(_ t: T.Type) { // The function in question
}
public func foo2(_ t: P.Type) { // Note that this works as expected, but is not generic
}
}
public func test() {
let x = X<P>()
x.foo1(C.self) // ERROR Cannot convert value of type 'C.Type' to expected argument type 'P.Protocol'
x.foo2(C.self) // Works as expected, but is not generic
}
Now, foo1 works fine when T is a class. However, when T is a protocol (e.g. P), Swift seems to rewrite my function signature from T.Type to T.Protocol.
Why did it do this?
How do I instead get foo1 to accept a type that inherits from P?
Class X is used in a number of other places - any changes to it must not restrict or remove class parameter T nor reduce X's functionality, nor make explicit reference to C or P. (It would be acceptable to constrain T to exclude protocols that do not extend AnyObject; I don't know how to do that, though. It might also be acceptable to e.g. create a subclass of X that adds the ability to handle a protocol in T, but I'm not sure how to do that, either.)
For clarity, this class is used to register classes (t) that conform to some specified parent (T), for more complicated project reasons. (Note that classes are being registered, not instances thereof.) The parent is given at the creation of X, via the type parameter. It works fine for a T of any class, but I'd also like it to work for a T of any protocol - or at least for a T of any protocol P: AnyObject, under which circumstances foo1 should accept any subclass of P...the same way it works when T is a class.

Even though C is a P, but C.Type != P.Type, so the error.
But generics works in a bit different way, like in below examples:
public class X {
public func foo1<T>(_ t: T.Type) { // this way
}
public func foo3<T:P>(_ t: T.Type) { // or this way
}
public func foo2(_ t: P.Type) { // Note that this works as expected, but is not generic
}
}
public func test() {
let x = X()
x.foo1(C.self) // << works
x.foo3(C.self) // << works
x.foo2(C.self) // Works as expected, but is not generic
}

Related

Why does a mutating method in a Swift protocol infinitely recurse unless only an extension method?

I came across the following code in SR-142 on bugs.swift.org.
If a protocol has an extension method that's mutating, a class instance can call the mutating function without any problem.
// protocol definition
protocol P { }
extension P {
mutating func m() { }
}
// class conforming to P
class C : P {
// redeclare m() without the mutating qualifier
func m() {
// call protocol's default implementation
var p: P = self
p.m()
}
}
let c = C()
c.m()
If I make a small change to add the method to the protocol declaration:
protocol P {
mutating func m() // This is what I added.
}
extension P {
mutating func m() { }
}
class C : P {
func m() {
var p: P = self
p.m()
}
}
let c = C()
c.m() // This one is calling itself indefinitely; why?
Why does c.m() keep calling itself again and again?
With your change in the second example, by including the m in the protocol definition, that instructs Swift to employ dynamic dispatch. So when you call p.m(), it dynamically determines whether the object has overridden the default implementation of the method. In this particular example, that results in the method recursively calling itself.
But in the first example, in the absence of the method being part of the protocol definition, Swift will employ static dispatch, and because p is of type P, it will call the m implementation in P.
By way of example, consider where the method is not part of the protocol definition (and therefore not in the “protocol witness table”):
protocol P {
// func method()
}
extension P {
func method() {
print("Protocol default implementation")
}
}
struct Foo: P {
func method() {
print(“Foo implementation")
}
}
Because the foo is a P reference and because method is not part of the P definition, it excludes method from the protocol witness table and employs static dispatch. As a result the following will print “Protocol default implementation”:
let foo: P = Foo()
foo.method() // Protocol default implementation
But if you change the protocol to explicitly include this method, leaving everything else the same, method will be included in the protocol witness table:
protocol P {
func method()
}
Then the following will now print “Foo implementation”, because although the foo variable is of type P, it will dynamically determine whether the underlying type, Foo, has overridden that method:
let foo: P = Foo()
foo.method() // Foo implementation
For more information on dynamic vs static dispatch, see WWDC 2016 video Understanding Swift Performance.
By declaring m in the protocol & providing implementation in your class, it over writes the default implementation.
But in the first example when you cast your class as protocol it will call protocol's default implementation because the implementation of the class is it own and not over writing any of the protocol's method

How should I write this in Swift?

Let's say that I create a protocol like this:
protocol A {
associatedtype T
func computeSomething(with:T) -> Double
}
In my generic typed class, I would like to do something like this:
class B<U> {
var doSomething:A<U>
}
This thing is that this generates an error, but I would like to accept any type that would support computeSomething on my type U but I don't know at all how to do that?
Edit for clarity
Basically if A was a generic struct or class, that would be possible, but what if no default implementation (provided by class or struct) makes sense here and the only thing I want is to ensure that the type does what I want?
Edit #2 (with concrete example)
I wanted to simplify my question which makes it pretty hard to understand so here is a still simplified and fictional problem that probably matches better the issue I am encountering:
I am writing a generic class that processes its generic type T:
class Process<T> { ... }
The class Process itself includes code that processes T, but in order for this code to work, it needs T to conform to some protocols, for instance:
protocol A {
func mixWith(other:A) -> A
}
protocol B {
var isFoo:Bool { get set }
}
So my first approach was to simply require T to conform to those protocols:
class Process<T:<A,B>> { ... }
This looks like the simplest approach and probably is in many cases, but in my case I think that this actually is problematic, for this reason:
First, I may need to process the same type in many different ways, and changing a way a type is being processed often requires changing the actual implementation of protocols A and B for instance in this case, fooProcess and barProcess are both of type Process with generic type MyType:
let fooProcess = Process<MyType>()
let barProcess = Process<MyType>()
But I want fooProcess and barProcess to do different operations which in many cases would require to change the implementation of the A and B protocols of my MyType type and that's simply not possible.
So my first idea was to simply require some closures and variables to be defined so that I wouldn't need protocols anymore and would define the way data is being processed only in my Process class, a little bit like this:
class Process<T> {
//
var mix:(_ lhs:T, _ rhs:T)->(T)
var isFoo:(_ a:T)->(Bool)
...
}
There all of the processing would be directly implemented in my Processing class, again this would have looked like the right solution but now comes another issue, which led me to my associated type approach: it turns out that in many cases, the user of my Process class would want to get some default behaviour implemented by my framework, for instance, I could automatically implement protocol A and B for them as long as their class conformed to protocol X, here is how it did it:
protocol X:A,B {
...
}
extension protocol X {
// Here was my default implementation of A and B, which enabled my user to directly get A and B implemented as long as their type conformed to X
}
By using this method, I would let my user directly choose what they wanted to implement themselves, by conforming to protocol X they would only need to write a little bit of code and let my framework to all of the rest by itself, and if they wanted to implement themselves A or B they still could.
So if I am right, there is no way to do such a thing with my closures implementation.
So for this reason, I thought that an associated type protocol would be a good solution because here I could let my users easily get some default behaviour or write their own, so now we are getting back to my original question:
protocol AProcessing {
associatedtype U
func mix(_ lhs:U, _ rhs:U) -> U
}
protocol BProcessing {
associatedtype U
func isFoo(_ a:U) -> Bool
}
And then do something like that:
class Process<T> {
var aProcessor:AProcessing<T>
var bProcessor:BProcessing<T>
}
Here the advantage compared to closures is that I could write a special class conforming to AProcessing that could provide default implementation, this way:
class AutomaticAProcessing<T:X>:AProcessing { ... }
That would have enabled my users to so something like that:
var processData = Process<SomeType>()
processData.aProcessor = AutomaticAProcessing<SomeType>()
processData.bProcessor = TheirOwnImplemtation
Not only is this not possible in Swift, but it also feels like I am using too many "hacks" to get things done and there should be an easier language feature to do that, unfortunately I don't know what I should use.
I don't think it is possible, because the generic type of the protocol is specified in the class that implements it.
You could write something like this:
class B<U, P: A> where P.T == U {
var someVar: P?
}
But then you would need to specify a second parameter with the specific class. For example:
class C: A {
typealias T = String
func computeSomething(with: String) -> Double {
return 0.0
}
}
let b = B<String, C>()
let c = b.someVar
But it can't return a protocol with specific type in its associatedtype
One way would be to start with an empty generic struct, and then extend it on types where it makes sense:
struct A<T> {}
extension A where T: Base {
func computeSomething(with: T) -> Double {
return 1
}
}
Usage:
protocol Base {}
class B<U: Base> {
let doSomething = A<U>()
func foo(x: U) -> Double {
return doSomething.computeSomething(with: x)
}
}
class Z : Base {}
let x = B<Z>()
let y = x.foo(x: Z())
print(y)
To EDIT #2:
Remove associated types, and it should be workable:
protocol A {}
protocol B {}
protocol AProcessing {
func mix(_ lhs: A, _ rhs: A) -> A
}
protocol BProcessing {
func isFoo(_ a: B) -> Bool
}
Then, your processor:
class Process<T: A & B> {
var aProcessor: AProcessing!
var bProcessor: BProcessing!
func foo(_ a: T) -> Bool {
let x = aProcessor.mix(a, a)
guard let b = x as? B else { return false }
return bProcessor.isFoo(b)
}
}
And usage:
struct AutomaticAProcessing : AProcessing {
func mix(_ lhs: A, _ rhs: A) -> A { return lhs }
}
struct TheirOwnImplemtation : BProcessing {
func isFoo(_ a: B) -> Bool { return false }
}
struct SomeType : A, B {}
var processData = Process<SomeType>()
processData.aProcessor = AutomaticAProcessing()
processData.bProcessor = TheirOwnImplemtation()
let x = SomeType()
let y = processData.foo(x)
print(y)

Generic Constraints and Initializer Inheritance in Swift

I am trying to call an initializer required by protocol A on a type that both conforms to A and is a subclass of C.
All is fine and good if C is a base class. But as soon as C subclasses another class, say B, it breaks.
Here's what I am talking about:
protocol A {
init(foo: String)
}
class B {
init() {}
}
class C: B {}
func makeSomething<T: A>() -> T where T: B {
return T(foo: "Hi")
}
That works. But if I change where T: B to where T: C, I get the following error: argument passed to call that takes no arguments. It will only allow me to call Bs init.
Why can't I call this initializer? I understand that the super class has its own designated initializers that must be called. But that will be enforced when someone actually writes the class that subclasses B and conforms to A. (E.g. implementing init(Foo: String) and calling super.init(bar: Int) inside).
Any help here would be greatly appreciated. Thanks!
Swift provides a default initializer for your base class if it has all properties initialized but not incase of Generics because Swift may think its properties has not been initialized.
So when you constraint your return type as
func makeSomething<T: A>() -> T where T: C
It requires initialization for class C as Swift cannot provide a default initializer.But if your remove the where clause everything works fine
func makeSomething<T: A>() -> T {
return T(foo:"string")
}
If you want to return return T(foo: "Hi") :
You are getting error because class C doesn't have initializer that accepts init(foo: String)
Change your class C as
class C: A {
required init(foo: String) {
}
}
which provides at least one initializer that accepts the argument type.
So, remember if you don't subclass There is already one initializer doing your job and you dont get error.

Function that takes a protocol and a conforming class (!) instance as parameters

I am trying to figure out how to define a function which takes the following
two parameters:
A protocol.
An instance of a class (a reference type) conforming to that protocol.
For example, given
protocol P { }
class C : P { } // Class, conforming to P
class D { } // Class, not conforming to P
struct E: P { } // Struct, conforming to P
this should compile:
register(proto: P.self, obj: C()) // (1)
but these should not compile:
register(proto: P.self, obj: D()) // (2) D does not conform to P
register(proto: P.self, obj: E()) // (3) E is not a class
It is easy if we drop the condition that the second parameter is a class instance:
func register<T>(proto: T.Type, obj: T) {
// ...
}
but this would accept the struct (value type) in (3) as well.
This looked promising and compiles
func register<T: AnyObject>(proto: T.Type, obj: T) {
// ...
}
but then none of (1), (2), (3) compile anymore, e.g.
register(proto: P.self, obj: C()) // (1)
// error: cannot invoke 'register' with an argument list of type '(P.Protocol, obj: C)'
I assume that the reason for the compiler error is the same as in
Protocol doesn't conform to itself?.
Another failed attempt is
func register<T>(proto: T.Type, obj: protocol<T, AnyObject>) { }
// error: non-protocol type 'T' cannot be used within 'protocol<...>'
A viable alternative would be a function which takes as parameters
A class protocol.
An instance of a type conforming to that protocol.
Here the problem is how to restrict the first parameter such that only
class protocols are accepted.
Background: I recently stumbled over the
SwiftNotificationCenter
project which implements a protocol-oriented, type safe notification mechanism.
It has a
register
method which looks like this:
public class NotificationCenter {
public static func register<T>(protocolType: T.Type, observer: T) {
guard let object = observer as? AnyObject else {
fatalError("expecting reference type but found value type: \(observer)")
}
// ...
}
// ...
}
The observers are then stored as weak references, and that's why they
must be reference types, i.e. instances of a class.
However, that is checked only at runtime, and I wonder how to make it a compile-time check.
Am I missing something simple/obvious?
You can't do what you are trying to do directly. It has nothing to do with reference types, it's because any constraints make T existential so it is impossible to satisfy them at the call site when you're referencing the protocol's metatype P.self: P.Protocol and an adopter C. There is a special case when T is unconstrained that allows it to work in the first place.
By far the more common case is to constrain T: P and require P: class because just about the only thing you can do with an arbitrary protocol's metatype is convert the name to a string. It happens to be useful in this narrow case but that's it; the signature might as well be register<T>(proto: Any.Type, obj: T) for all the good it will do.
In theory Swift could support constraining to metatypes, ala register<T: AnyObject, U: AnyProtocol where T.Type: U>(proto: U, obj: T) but I doubt it would be useful in many scenarios.

inheritance and polymorphism in swift

I have the following problem:
Class A - super class.
Class A protocol:
has method ->
func test(params: GeneralParams, completionBlock: GeneralCompletionBlock)
GeneralParams is super class and has the following 2 subclasses: BParams, CParams.
now i have 2 more classes:
class B: A, A protocol
class C: A, A protocol
I want class B, C to use test function but with different class for their params for class B i want to use BParams and different completion block and for C the same thing. i want to have the same method for both with different parameters and implementation for both.
Whats the best solution for this situation?
Since Swift allows method overload, the best practice here would be to overload a method to suite your needs in a new class.
For instance:
class B: A{
func test(params: BParams, completionBlock: GeneralCompletionBlock){
...
}
}
class C: A{
func test(params: CParams, completionBlock: GeneralCompletionBlock){
...
}
}
I want class B, C to use test function but with different class for their params
The most important rule of subclassing is substitutability. If B is a kind of A, then everywhere an A can be used, it must be legal to use a B. So every subclass of A must support this method:
func test(params: GeneralParams, completionBlock: GeneralCompletionBlock)
It cannot restrict this method to other types (not even to subtypes of these). If it did, then there would be places that I could use A, but couldn't use B.
B is free to extend A and add new methods. So, as #tmac_balla suggests, you can add an overload that will be selected for subtypes. For example:
class AParam {}
class BParam: AParam {}
class A {
func f(param: AParam) {print("A")}
}
class B: A {
func f(param: BParam) {print("B")}
}
B().f(AParam()) // "A"
B().f(BParam()) // "B"
But B must still support being passed the superclass.
Most of the time in Swift, this is the wrong way to go about things. Subclassing introduces lots of complexity that you usually don't want. Instead you generally want protocols and generics in Swift. For example, rather than A, B, and C, you would just have a generic A and a protocol.
protocol Param {
var name: String { get }
}
struct AParam: Param {
let name = "A"
}
struct BParam: Param {
let name = "B"
}
struct S<P: Param> {
func f(param: P) {print(param.name)}
}
let a = S<AParam>()
a.f(AParam()) // "A"
// a.f(BParam()) // error; we've restricted the type it can take
let b = S<BParam>()
b.f(BParam()) // "B"