Fast protocols and return types for global functions

This is a question to the following question: The func function returns Self . The protocol is as follows:

protocol Copyable { init(copy: Self) func copy() -> Self } 

The following works fine, but the copy() function is exactly the same for each implementation, namely

 func copy() -> Self { return self.dynamicType(copy: self) } 

According to this http://nshipster.com/swift-default-protocol-implementations/ I tried the global function

 func copy<T : Copyable>(makeCopy: T) -> T { return makeCopy.dynamicType(copy: makeCopy) } 

However, when it calls in a class that implements the protocol below

 protocol Mutatable : Copyable { func mutated() -> Self } class C : Mutatable { var a = 0 required init(_ a: Int) { self.a = a } required init(copy: C) { a = copy.a } func mutated() -> Self { let mutated = copy(self) mutated.a++ return mutated // error: 'C' is not convertible to 'Self' } } 

I get the error as indicated. When I type mutated , autocomplete shows mutated as (C) , and I don't know what that means. I also tried adding required to func mutated() , but apparently required is only allowed for inits . Any way to make this work?

+6
swift
Sep 05 '14 at 23:03
source share
1 answer

This question has the same form as the copy, and the same solution. Make the mutation an initializer, not a method.

 protocol Copyable { init(copy: Self) } protocol Mutatable : Copyable { init(byMutating: Self) } class C : Mutatable { var a = 0 required init(_ a: Int) { self.a = a } required init(copy: C) { a = copy.a } required convenience init(byMutating: C) { self.init(copy: byMutating) self.a++ } } // These are purely for convenience func copy<T : Copyable>(x: T) -> T { return x.dynamicType(copy: x) } func mutated<T: Mutatable>(x: T) -> T { return x.dynamicType(byMutating: x) } 

But to repeat Matt's point in a related article, you can have the C(copy: x) syntax pretty handy, and you can have the copy(x) syntax pretty handy, and there is always x.dynamicType(copy: x) . But you cannot have x.copy() syntax without any annoying work. You need to either duplicate func copy() -> Self { return copy(self) } in each class, or you need to create some specific class that implements this method, and C will ultimately inherit. This is currently the main limitation of Swift. I agree with Matt in diagnosing possible solutions and suspect that in the future some kind of trait system will probably be added, possibly according to Scala.

Itโ€™s worth paying attention to Mattโ€™s comment that โ€œall this underlines the significant tension between methods and functions in Swiftโ€. This is another way of saying that there is tension between the object-oriented paradigm and the functional paradigm, and moving between them can lead to some outages. Languages โ€‹โ€‹try to solve this problem with various functions, but there are important differences between objects with messages and properties, vs functions with data and combinators, and "getting the best of both worlds" can sometimes create some rough edges.

It's easy to forget that when comparing Swift with other languages, there is a big difference between v0.9 and v2.11. Many things that we take for granted in our favorite languages โ€‹โ€‹also did not exist in their v1.




In your comment, you might think that mutated is of type Self . But this is type C , as your autocomplete indicates. As before, C does not match Self if you cannot promise that there are no subclasses ( C as final or struct). Swift types are allowed at compile time, and not at run time, unless you use dynamicType .

To be more specific, Swift looks at this line:

  let mutated = copy(self) 

He notes that copy is common to the type of its parameter, and he must build a copy version at compile time for the call. No type of Self . It is just a placeholder and should be allowed at compile time. Type Self in this lexical domain C Therefore, it builds copy<C> . But if you subclass C , it may be the wrong function (and in this case it will). This is very closely related to: https://stackoverflow.com/a/3122/2/

The fact that the autocomplete type says (C) , not C , is a secondary side effect of Swift functions and tuples, and quite often appears, but I have yet to meet a case where it really matters, a Swift function such as func f(x: Int, y:Int) , in fact, does not have two parameters. It has one parameter from 2 elements of type (Int, Int) . This fact is important for how the currying syntax works (see Swift for more information on currying in Swift). Therefore, when you specialize in copy , you specialize in it with a 1-tuple of type (C) . (Or perhaps the compiler is simply trying to do this as one of various attempts, and this is only the one it reports to.) In Swift, any value can be trivially replaced with one tuple of the same type. So the copy return is actually a 1-tuple of C written by (C) . I suspect that the Swift compiler will improve its messages over time to remove extraneous parentheses, but sometimes they appear.

+1
Sep 06 '14 at
source share



All Articles