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

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

Related

How is init() relevant in Swift protocol?

Protocols in Swift can declare the init() method in their definition. However, I can't think of any use case where this solves any problem other than forcing the conforming classes to define the init() as in the protocol. We can call the declared methods on the protocol type but init on protocol cannot be used to instantiate its object, which is its only purpose.
What problem does declaring init() method in a protocol solve?
I think the real utility comes when it's used as a constraint in a generic class o function. This is real code from one of my projects.
I declare a protocol with a init:
protocol JSONCreatable {
init(fromJson json: JSON)
}
Then, in a generic function where I return a class that conforms to that protocol:
import SwiftyJSON
extension JSON {
func asObject<T>() -> T? where T: JSONCreatable {
if isEmpty {
return nil
}
return T(fromJson: self)
}
func asArray<T>() -> [T] where T: JSONCreatable {
return array?.map{ json in T(fromJson: json) } ?? []
}
}
This allows me to do things like this:
let user: User = json["user"].asObject()
let results: [Element] = json["elements"].asArray()
It forces class to have init(data: data) from some data, example:
protocol JSONable {
init(data: JSON)
}
forces all classes, that are JSONable to have an initialiser from JSON, so you are always sure, that you can create an instance from JSON.
It's commonly used in order to allow for protocol extensions and generic placeholders constrained to protocols to call the initialiser on the given concrete type that conforms to the protocol. For example, consider RangeReplaceableCollection's default implementation of init<S : Sequence>(_ elements: S):
extension RangeReplaceableCollection {
// ...
/// Creates a new instance of a collection containing the elements of a
/// sequence.
///
/// - Parameter elements: The sequence of elements for the new collection.
public init<S : Sequence>(_ elements: S) where S.Iterator.Element == Iterator.Element {
self.init()
append(contentsOf: elements)
}
// ...
}
Without init() being defined as a protocol requirement of RangeReplaceableCollection, there's no way for the extension to know that we can call init() in order to create a new instance of the conforming type.
But it can also be used directly outside of generics and extensions – for example, it can be used to construct a new instance represented by a given existential metatype (the metatype of 'some concrete type that conforms to a protocol'):
protocol P {
init()
}
struct S : P {
init() {}
}
let s: P = S()
let s1 = type(of: s).init() // creates a new instance of S, statically typed as P.
In this example:
type(of: s) returns the dynamic type of s as P.Type (an existential metatype), as s is statically typed as P. Remember that type(of:) is a (T) -> T.Type operation.
init() constructs a new instance of the underlying concrete type, in this case S.
The new instance is statically typed as P (i.e boxed in an existential container).

Make a type itself -- not its instances -- conform to a protocol

I was wondering if it is possible in Swift to make a type conform to a protocol, so that I can treat the type itself as conforming to a protocol the way one normally treats instances as conforming to a protocol. Example code:
protocol P {
func f()
}
class C where C.self: P { // Not actual code
static func f() {
print("Because C.f: ()->() exists, C.self should satisfy the protocol.")
}
}

How do I declare a generic function in a protocol that returns self? [duplicate]

I have a protocol P that returns a copy of the object:
protocol P {
func copy() -> Self
}
and a class C that implements P:
class C : P {
func copy() -> Self {
return C()
}
}
However, whether I put the return value as Self I get the following error:
Cannot convert return expression of type 'C' to return type 'Self'
I also tried returning C.
class C : P {
func copy() -> C {
return C()
}
}
That resulted in the following error:
Method 'copy()' in non-final class 'C' must return Self to conform
to protocol 'P'
Nothing works except for the case where I prefix class C with final ie do:
final class C : P {
func copy() -> C {
return C()
}
}
However if I want to subclass C then nothing would work. Is there any way around this?
The problem is that you're making a promise that the compiler can't prove you'll keep.
So you created this promise: Calling copy() will return its own type, fully initialized.
But then you implemented copy() this way:
func copy() -> Self {
return C()
}
Now I'm a subclass that doesn't override copy(). And I return a C, not a fully-initialized Self (which I promised). So that's no good. How about:
func copy() -> Self {
return Self()
}
Well, that won't compile, but even if it did, it'd be no good. The subclass may have no trivial constructor, so D() might not even be legal. (Though see below.)
OK, well how about:
func copy() -> C {
return C()
}
Yes, but that doesn't return Self. It returns C. You're still not keeping your promise.
"But ObjC can do it!" Well, sort of. Mostly because it doesn't care if you keep your promise the way Swift does. If you fail to implement copyWithZone: in the subclass, you may fail to fully initialize your object. The compiler won't even warn you that you've done that.
"But most everything in ObjC can be translated to Swift, and ObjC has NSCopying." Yes it does, and here's how it's defined:
func copy() -> AnyObject!
So you can do the same (there's no reason for the ! here):
protocol Copyable {
func copy() -> AnyObject
}
That says "I'm not promising anything about what you get back." You could also say:
protocol Copyable {
func copy() -> Copyable
}
That's a promise you can make.
But we can think about C++ for a little while and remember that there's a promise we can make. We can promise that we and all our subclasses will implement specific kinds of initializers, and Swift will enforce that (and so can prove we're telling the truth):
protocol Copyable {
init(copy: Self)
}
class C : Copyable {
required init(copy: C) {
// Perform your copying here.
}
}
And that is how you should perform copies.
We can take this one step further, but it uses dynamicType, and I haven't tested it extensively to make sure that is always what we want, but it should be correct:
protocol Copyable {
func copy() -> Self
init(copy: Self)
}
class C : Copyable {
func copy() -> Self {
return self.dynamicType(copy: self)
}
required init(copy: C) {
// Perform your copying here.
}
}
Here we promise that there is an initializer that performs copies for us, and then we can at runtime determine which one to call, giving us the method syntax you were looking for.
With Swift 2, we can use protocol extensions for this.
protocol Copyable {
init(copy:Self)
}
extension Copyable {
func copy() -> Self {
return Self.init(copy: self)
}
}
There is another way to do what you want that involves taking advantage of Swift's associated type. Here's a simple example:
public protocol Creatable {
associatedtype ObjectType = Self
static func create() -> ObjectType
}
class MyClass {
// Your class stuff here
}
extension MyClass: Creatable {
// Define the protocol function to return class type
static func create() -> MyClass {
// Create an instance of your class however you want
return MyClass()
}
}
let obj = MyClass.create()
Actually, there is a trick that allows to easily return Self when required by a protocol (gist):
/// Cast the argument to the infered function return type.
func autocast<T>(some: Any) -> T? {
return some as? T
}
protocol Foo {
static func foo() -> Self
}
class Vehicle: Foo {
class func foo() -> Self {
return autocast(Vehicle())!
}
}
class Tractor: Vehicle {
override class func foo() -> Self {
return autocast(Tractor())!
}
}
func typeName(some: Any) -> String {
return (some is Any.Type) ? "\(some)" : "\(some.dynamicType)"
}
let vehicle = Vehicle.foo()
let tractor = Tractor.foo()
print(typeName(vehicle)) // Vehicle
print(typeName(tractor)) // Tractor
Swift 5.1 now allow a forced cast to Self, as! Self
1> protocol P {
2. func id() -> Self
3. }
9> class D : P {
10. func id() -> Self {
11. return D()
12. }
13. }
error: repl.swift:11:16: error: cannot convert return expression of type 'D' to return type 'Self'
return D()
^~~
as! Self
9> class D : P {
10. func id() -> Self {
11. return D() as! Self
12. }
13. } //works
Following on Rob's suggestion, this could be made more generic with associated types. I've changed the example a bit to demonstrate the benefits of the approach.
protocol Copyable: NSCopying {
associatedtype Prototype
init(copy: Prototype)
init(deepCopy: Prototype)
}
class C : Copyable {
typealias Prototype = C // <-- requires adding this line to classes
required init(copy: Prototype) {
// Perform your copying here.
}
required init(deepCopy: Prototype) {
// Perform your deep copying here.
}
#objc func copyWithZone(zone: NSZone) -> AnyObject {
return Prototype(copy: self)
}
}
I had a similar problem and came up with something that may be useful so I though i'd share it for future reference because this is one of the first places I found when searching for a solution.
As stated above, the problem is the ambiguity of the return type for the copy() function. This can be illustrated very clearly by separating the copy() -> C and copy() -> P functions:
So, assuming you define the protocol and class as follows:
protocol P
{
func copy() -> P
}
class C:P
{
func doCopy() -> C { return C() }
func copy() -> C { return doCopy() }
func copy() -> P { return doCopy() }
}
This compiles and produces the expected results when the type of the return value is explicit. Any time the compiler has to decide what the return type should be (on its own), it will find the situation ambiguous and fail for all concrete classes that implement the P protocol.
For example:
var aC:C = C() // aC is of type C
var aP:P = aC // aP is of type P (contains an instance of C)
var bC:C // this to test assignment to a C type variable
var bP:P // " " " P " "
bC = aC.copy() // OK copy()->C is used
bP = aC.copy() // Ambiguous.
// compiler could use either functions
bP = (aC as P).copy() // but this resolves the ambiguity.
bC = aP.copy() // Fails, obvious type incompatibility
bP = aP.copy() // OK copy()->P is used
In conclusion, this would work in situations where you're either, not using the base class's copy() function or you always have explicit type context.
I found that using the same function name as the concrete class made for unwieldy code everywhere, so I ended up using a different name for the protocol's copy() function.
The end result is more like:
protocol P
{
func copyAsP() -> P
}
class C:P
{
func copy() -> C
{
// there usually is a lot more code around here...
return C()
}
func copyAsP() -> P { return copy() }
}
Of course my context and functions are completely different but in spirit of the question, I tried to stay as close to the example given as possible.
Just throwing my hat into the ring here. We needed a protocol that returned an optional of the type the protocol was applied on. We also wanted the override to explicitly return the type, not just Self.
The trick is rather than using 'Self' as the return type, you instead define an associated type which you set equal to Self, then use that associated type.
Here's the old way, using Self...
protocol Mappable{
static func map() -> Self?
}
// Generated from Fix-it
extension SomeSpecificClass : Mappable{
static func map() -> Self? {
...
}
}
Here's the new way using the associated type. Note the return type is explicit now, not 'Self'.
protocol Mappable{
associatedtype ExplicitSelf = Self
static func map() -> ExplicitSelf?
}
// Generated from Fix-it
extension SomeSpecificClass : Mappable{
static func map() -> SomeSpecificClass? {
...
}
}
To add to the answers with the associatedtype way, I suggest to move the creating of the instance to a default implementation of the protocol extension. In that way the conforming classes won't have to implement it, thus sparing us from code duplication:
protocol Initializable {
init()
}
protocol Creatable: Initializable {
associatedtype Object: Initializable = Self
static func newInstance() -> Object
}
extension Creatable {
static func newInstance() -> Object {
return Object()
}
}
class MyClass: Creatable {
required init() {}
}
class MyOtherClass: Creatable {
required init() {}
}
// Any class (struct, etc.) conforming to Creatable
// can create new instances without having to implement newInstance()
let instance1 = MyClass.newInstance()
let instance2 = MyOtherClass.newInstance()

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.

swift how to define abstract class and why apple invent associated type but not use generic protocol

I am a swift beginner. Something puzzled me when learning. Now I want to define an abstract class or define some pure virtual method, but I cannot find a way to do it. I have a protocol with associated type(this also puzzled me, why not use generic protocol), and some methods need to be implemented in a base class, and other classes inherited from the base class, they should implement other methods in the protocol, how can I do?
for instance:
Protocol P{
typealias TypeParam
func A()
func B()
}
class BaseClass<TypeParam> : P {
abstract func A()
func B(){
if someCondition {
A()
}
}
}
class ChildClass : BaseClass<Int> {
func A(){}
}
It seems very strange, and I still cannot find a method to resolve the abstract problem.
Swift has something similar: protocol extensions
They can define default implementations so you don't have to declare the method in your base class but it also doesn't force to do that in any class, struct or enum.
protocol P {
associatedtype TypeParameter
func A()
func B()
}
extension P {
func A(){}
}
class BaseClass<TypeParam> : P {
typealias TypeParameter = TypeParam
func B(){
if someCondition {
A()
}
}
}
class ChildClass : BaseClass<Int> {
// implementation of A() is not forced since it has a default implementation
func A(){}
}
Another approach would be to use a protocol instead of BaseClass which is more in line with protocol oriented programming:
protocol Base {
associatedtype TypeParameter
func A()
func B()
}
extension Base {
func B(){
if someCondition {
A()
}
}
}
class ChildClass : Base {
typealias TypeParameter = Int
// implementation of A() is forced but B() is not forced
func A(){}
}
However one of the big disadvantages would be that a variable of protocol type can only be used in generic code (as generic constraint):
var base: Base = ChildClass() // DISALLOWED in every scope
As a workaround for this limitation you can make a wrapper type:
// wrapper type
struct AnyBase<T>: Base {
typealias TypeParameter = T
let a: () -> ()
let b: () -> ()
init<B: Base>(_ base: B) where B.TypeParameter == T {
// methods are passed by reference and could lead to reference cycles
// below is a more sophisticated way to solve also this problem
a = base.A
b = base.B
}
func A() { a() }
func B() { b() }
}
// using the wrapper:
var base = AnyBase(ChildClass()) // is of type AnyBase<Int>
Regarding the use of "true" generic protocols, the Swift team has chosen to use associatedtype because you can use many generic types without having to write all out in brackets <>.
For example Collection where you have an associated Iterator and Index type. This allows you to have specific iterators (e.g. for Dictionary and Array).
In general, generic/associated types are good for code optimization during compilation but at the same time being sometimes too static where you would have to use a generic wrapper type.
A useful link to some patterns for working with associated types.
(See also above)
A more sophisticated way to solve the problem of passing the methods by reference.
// same as `Base` but without any associated types
protocol _Base {
func A()
func B()
}
// used to store the concrete type
// or if possible let `Base` inherit from `_Base`
// (Note: `extension Base: _Base {}` is currently not possible)
struct BaseBox<B: Base>: _Base {
var base: B
init(_ b: B) { base = b}
func A() { base.A() }
func B() { base.B() }
}
struct AnyBase2<T>: Base {
typealias TypeParameter = T
var base: _Base
init<B: Base>(_ base: B) where B.TypeParameter == T {
self.base = BaseBox(base)
}
func A() { base.A() }
func B() { base.B() }
}
// using the wrapper:
var base2 = AnyBase2(ChildClass()) // is of type AnyBase2<Int>