How to use a protocol with associative type as return value in Swift? - swift

I'm trying to use a protocol with associated type as return value of a class func.
Another post here on stackoverflow is about the same question and based on this post's answer I tried the following:
// addArg takes a single Int argument and returns something,
// which is capable of adding a another Int argument
class OpWithNoArg {
func addArg<OP: OpWithOneArg where OP.ArgType == Int>(arg: Int) -> OP {
return OpWithOneArgImpl() // (1) error here !
}
}
// a protocol with associated type, which allows adding another
// argument of a not yet specified type
protocol OpWithOneArg {
typealias ArgType
func addAnotherArg(arg: ArgType)
}
// an implementation of the protocol above,
// which fixes the associated type to an Int
class OpWithOneArgImpl : OpWithOneArg {
typealias ArgType = Int
func addAnotherArg(arg: Int) {
// ...
}
}
The error in Xcode 7.0 Beta 4 on line marked with (1) is
cannot convert return expression of type 'OpWithOneArgImpl' to
expected return type 'OP'
If I change the return value to an optional and nil is returned, the sample compiles successfully:
// return null
class OpWithNoArg {
func addArg<OP: OpWithOneArg where OP.ArgType == Int>(arg: Int) -> OP? {
// return OpWithOneArgImpl()
return nil
}
}
Is this way of using a protocol with associative type as return value possible in swift, and if that's the case, how can the compiler error mentioned above be fixed?
Thanks in advance!
EDIT:
In Java, the code would be something like the following snippet. I try to find a way in Swift to achieve the same.
class C {
<T> P<T> f(T arg) {
return new PImpl();
}
}
interface P<S> {
void g(S arg);
}
class PImpl<S> implements P<S> {
PImpl() {}
public void g(S arg) {}
}

Not clear what you are trying to achieve, but imho the error stems from the fact that in your addArg function you define a generic type OP which should be typically used in the function's arguments and body.
Instead you return a non generic type trying to force it to be treated as a generic `OP.
A quick fix could be a force cast to OP of your return object:
return OpWithOneArgImpl() as !OP
but I wouldn't recommend it.
In your example your addArg will always return a OpWithOneArgImpl thus it could just be defined as a func addArg(arg: Int) -> OpWithOneArgImpl
Hope this helps.
EDIT:
Probably this is not what you're trying to achieve, but hope it can help
with a clearer explanation of what I intended above.
protocol OpWithOneArg {
typealias ArgType
func addAnotherArg(arg: ArgType)
init() // Must have a init with no argument
}
class OpWithOneArgImpl : OpWithOneArg {
typealias ArgType = Int
func addAnotherArg(arg: Int) {
// ...
}
required init() {
// init implementation
}
}
class OpWithNoArg {
func addArgWithOPType<OP: OpWithOneArg where OP.ArgType == Int>(op: OP.Type, arg: Int) -> OP {
return OP() // Here we use the init
}
}
let foo = OpWithNoArg()
foo.addArgWithOPType(OpWithOneArgImpl.self, arg: 3)
Edit:
Maybe you might investigate the use of generic types:
protocol OpWithOneArg {
typealias ArgType
func addAnotherArg(arg: ArgType)
init() // Must have a init with no argument
}
class OpWithOneArgImpl<T> : OpWithOneArg { // Generic implementation confirming to protocol
typealias ArgType = T
func addAnotherArg(arg: T) {
// ...
}
required init() {
// init implementation
}
}
class OpWithNoArg {
func addArgWithOPType<OP: OpWithOneArg>(op: OP.Type, arg: OP.ArgType) -> OP {
return OP() // Here we use the init
}
}
let foo = OpWithNoArg()
// OpWithOneArgImpl<Int>
foo.addArgWithOPType(OpWithOneArgImpl.self, arg: 3)
// OpWithOneArgImpl<String>
foo.addArgWithOPType(OpWithOneArgImpl.self, arg: "Hello")

Related

How do I override methods that have associated type parameters in subclasses?

What I tried to do:
protocol HasElement {
associatedtype ItemType
func getElement() -> ItemType
func setElement(element: ItemType)
}
class Element {}
class BarElement: Element {}
class Foo: NSObject, HasElement {
typealias ItemType = Element
func getElement() -> Element { ... }
func setElement(element: Element) { ... }
}
class Bar: Foo {
typealias ItemType = BarElement
override func getElement() -> BarElement { ... } // This works.
override func setElement(element: BarElement) { ... } // This fails.
}
The error is:
Method does not override any method from its superclass
If I try to use ItemType instead:
override func setElement(element: ItemType) { ... } // Still fails.
The error is:
'ItemType' is ambiguous for type lookup in this context
Is there a way to make this work?
Here's a way to do what you want:
protocol HasElement {
associatedtype ItemType
func getElement() -> ItemType
func setElement(element: ItemType)
}
class Element {}
class BarElement: Element {}
class Foo: HasElement {
// no need for typealias, the associated type is inferred
func getElement() -> Element { return Element() }
func setElement(element: Element) { }
}
class Bar: Foo {
// no need for typealias, the associated type is inferred
override func getElement() -> BarElement { return BarElement() }
// hide the parent class method
#available(*, unavailable, message: "Use setElement(element: BarElement)")
override func setElement(element: Element) { }
// comply with protocol in this class
func setElement(element: BarElement) { }
}
// can't do this now:
let myElement = Element()
let myBar = Bar()
myBar.setElement(element: myElement) // Error: 'setElement(element: BarElement)' is unavailable: Use setElement(element: BarElement)
The problem here isn't associated types, it's that method inputs are contravariant. Therefore you cannot override a method that expects a given superclass instance input with a method that expects a subclass instance input.
In fact, you can simply boil your code down to:
class Element {}
class BarElement : Element {}
class Foo {
func setElement(element: Element) { }
}
class Bar : Foo {
// error: Method does not override any method from its superclass
override func setElement(element: BarElement) { }
}
You simply cannot override an (Element) -> Void method with a (BarElement) -> Void method. The reasons for this should be fairly obvious if you consider what would happen if you created a Bar instance and upcast it to Foo. You're now able to pass an Element instance to a method that expects a BarElement instance, which is illegal.
The reason it works for your getElement method is that method outputs are covariant. Therefore overriding a () -> Element method with a () -> BarElement method is perfectly legal, as even if you upcast a Bar instance to Foo, the BarElement instance returned from getElement can be freely upcast to Element.
As for solutions, it rather depends on your exact use case. It may well be that what you're trying to do doesn't require inheritance at all, and instead you can just conform Foo and Bar to HasElement separately.
This works:
override func getElement() -> BarElement { ... }
using override keyword is when function signature does not change (BarElement is still Element). You copy and paste function from super class with same function name, same parameter name and same return value type.
But
override func setElement(element: BarElement) { ... }
fails because when you change parameter type (with same parameter name).
This case is overloading not overriding.

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()

class function with generic return value

Right now I have the following classes:
class A {
class func instantiate() -> A {
return MakeObject()
}
}
class B: A {}
let x = B.instantiate()
This results in x being of type A. How can I change instantiate in order to return an instance of the subclass that was called from? In other words, so that x ends up being of type B.
EDIT:
This is what I used to solve it, based on Martin R's answers:
class A {
class func instantiate() -> Self {
func helper<T>() -> T {
return MakeObject() as! T
}
return helper()
}
}
The returns type needs to be Self (which is the concrete type when
the class method is called), and initialization must be done with a
required init method (which can be overridden in a subclass):
class A {
class func instantiate() -> Self {
return self.init()
}
required init() {
}
}
class B: A {}
let x = B.instantiate() // `x` has type `B`
Alternatively, just define an init method
init(parameters ...) {
}
which "automatically" returns instances of the class that is is
called on.

Swift protocol extension implementing another protocol with shared associated type

Consider the following:
protocol Foo {
typealias A
func hello() -> A
}
protocol FooBar: Foo {
func hi() -> A
}
extension FooBar {
func hello() -> A {
return hi()
}
}
class FooBarClass: FooBar {
typealias A = String
func hi() -> String {
return "hello world"
}
}
This code compiles. But if I comment out explicit definition of associated type typealias A = String, then for some reason, swiftc fails to infer the type.
I'm sensing this has to do with two protocols sharing the same associated type but without a direct assertion through, for example, type parameterization (maybe associated type is not powerful/mature enough?), which makes it ambiguous for type inference.
I'm not sure if this is a bug / immaturity of the language, or maybe, I'm missing some nuances in protocol extension which rightfully lead to this behaviour.
Can someone shed some light on this?
look at this example
protocol Foo {
typealias A
func hello() -> A
}
protocol FooBar: Foo {
typealias B
func hi() -> B
}
extension FooBar {
func hello() -> B {
return hi()
}
}
class FooBarClass: FooBar {
//typealias A = String
func hi() -> String {
return "hello world"
}
}
with generics
class FooBarClass<T>: FooBar {
var t: T?
func hi() -> T? {
return t
}
}
let fbc: FooBarClass<Int> = FooBarClass()
fbc.t = 10
fbc.hello() // 10
fbc.hi() // 10
Providing explicit values for associated types in a protocol is required for conformance to said protocol. This can be accomplished by hard coding a type, as you've done with typealias A = String, or using a parameterized type as you mentioned, such as below:
class FooBarClass<T>: FooBar {
typealias A = T
...
}
Swift will not infer your associated type from an implemented method of the protocol, as there could be ambiguity with multiple methods with mismatching types. This is why the typealias must be explicitly resolved in your implementing class.

Extending the SequenceType in Swift

I wondered why map() and filter() in SequenceType return both an Array.
Actually, I don't think that's necessary. Returning a sequence again feels much more sensible to me.
However, I got stuck when trying to add sequential versions. Here's my attempt with map:
extension SequenceType {
func seqMap<T, S: SequenceType where S.Generator.Element == T>(
transform: Self.Generator.Element -> T) -> S
{
var sourceGen = generate()
let tGen: AnyGenerator<T> = anyGenerator {
if let el = sourceGen.next() {
return transform(el)
} else {
return nil
}
}
return AnySequence { tGen }
}
}
XCode tells me at the last return statement the following error:
cannot invoke initializer for type 'AnySequence<T>' with an argument list of type '(() -> AnyGenerator<T>)'
note: overloads for 'AnySequence<T>' exist with these partially matching parameter lists: (S), (() -> G)
Actually, my tGen is of type () -> G, so why does XCode think it is ambiguous?
The problem becomes more apparent if you split the return statement:
let tSeq = AnySequence { tGen }
return tSeq // error: cannot convert return expression of type 'AnySequence<T>' to return type 'S'
The compiler would infer the placeholder type S from the context
of a method call, and that could be any sequence
type with element type T, and not necessarily an AnySequence.
Here is a simple example demonstrating the same problem:
protocol MyProtocol { }
struct MyType { }
extension MyType : MyProtocol { }
func foo<P : Protocol>() -> P {
return MyType() // error: cannot convert return expression of type 'MyType' to return type 'P'
}
To solve the problem, change the return type to AnySequence<T>
and drop the generic type S:
extension SequenceType {
func seqMap<T>(transform: Self.Generator.Element -> T) -> AnySequence<T>
{
var sourceGen = generate()
let tGen: AnyGenerator<T> = anyGenerator {
if let el = sourceGen.next() {
return transform(el)
} else {
return nil
}
}
return AnySequence { tGen }
}
}
which can be written more compactly as
extension SequenceType {
func seqMap<T>(transform: Self.Generator.Element -> T) -> AnySequence<T>
{
var sourceGen = generate()
return AnySequence(anyGenerator {
sourceGen.next().map(transform)
})
}
}
using the map() method of the Optional type.
But note that SequenceType already has a lazy method which returns
a LazySequenceType:
/// A sequence containing the same elements as a `Base` sequence, but
/// on which some operations such as `map` and `filter` are
/// implemented lazily.
///
/// - See also: `LazySequenceType`
public struct LazySequence<Base : SequenceType>
and you can use
someSequence.lazy.map { ... }
to get a (lazily evaluated) sequence of the mapped values.