"Ambiguos use of function" when overloading a generic function in Swift - swift

Below you'll see a playground where I try to illustrate my problem. I have a class (Foo) with some overloaded generic methods (bar<T>()) whose return types depends on the generic parameter. I have 3 methods:
One must return a subclass of a specific type (in the example below it is called BaseClass)
Other one must return a Array of instances of this specific type.
The last should be used as default one if the other two conditions are not fulfilled.
The problem comes when I call the method. Even if I tell the compiler what is the type I am expecting, it gives me the error Ambiguos use of bar().
import UIKit
class BaseClass {
required init() {}
}
class Foo {
// 1
func bar<T: BaseClass>() -> T? {
// Just a default implementation, it should do real work inside
return T()
}
// 2
func bar<T: BaseClass>() -> [T]? {
// Just a default implementation, it should do real work inside
return []
}
// 3
func bar<T>() -> T? {
// Just a default implementation, it should do real work inside
return "Test" as? T
}
}
let foo = Foo()
// Should call "1", because it return type is BaseClass
let baseClassObject: BaseClass? = foo.bar()
// Should call "2", because it return type is [BaseClass]
let baseClasArray: [BaseClass]? = foo.bar()
// Should call "3", because it return type is neither BaseClass nor [BaseClass]
let anyOtherObject: String = foo.bar()
In my opinion, the compiler should know what method call, cause I am telling it the return type, right? is this a limitation with generics or am I missing something?
Thanks in advance.
Update 19th April
In an answer they say that the conflicts comes because "BaseClass" can be interpreted as BaseClass but also as T. But in this modified playground, the compilers does infers which method use properly. Is there any difference? should I have the same conflict here?:
import UIKit
class BaseClass {
required init() {}
}
class Foo {
// 1
func bar<T: BaseClass>(param: T) -> String {
return "I am BaseClass"
}
// 2
func bar<T: BaseClass>(param: [T]) -> String {
return "I am [BaseClass]"
}
// 3
func bar<T>(param: T) -> String {
return "I am other thing"
}
}
let foo = Foo()
// It prints "I am BaseClass"
foo.bar(BaseClass())
// It prints "I am [BaseClass]"
foo.bar([BaseClass(), BaseClass()])
// It prints "I am another thing"
foo.bar(NSObject())

Number 3 causes conflict with the other functions
This is because < T > could also be of type BaseClass and of type [BaseClass]. The compiler simply sees an interface of "throw anything in here" which will conflict with any more specific level of specificity.

Related

Generics and a method returning a specialised generic instance

I am trying to create a method that will return a specialized instance of some generic type. Let's assume the following example:
class Base { }
class Foo: Base {}
class Bar: Base {}
class MyGenericView<T> where T: Base {
func show(with item: T) { }
}
class MySpecializedViewFoo: MyGenericView<Foo> { }
class MySpecializedViewBar: MyGenericView<Bar> { }
With the above given, I would like to have a function like:
func createView<T: Base>(for item: T) -> MyGenericView<T>
but when I try to implement it like
func createView<T: Base>(for item: T) -> MyGenericView<T> {
if item is Foo {
return MySpecializedViewFoo()
} else if item is Bar {
return MySpecializedViewBar()
}
fatalError("Unsupported item")
}
Then I receive
"Cannot convert return expression of type 'MyGenericView' to return type 'MyGenericView'" error.
Is that something that can be achieved? Could somebody please take a look and point an error in my understanding of that?
You can forcibly make the compiler trust you by doing:
func createView<T: Base>(for item: T) -> MyGenericView<T> {
if item is Foo {
return MySpecializedViewFoo() as! MyGenericView<T>
} else if item is Bar {
return MySpecializedViewBar() as! MyGenericView<T>
}
fatalError("Unsupported item")
}
The compiler doesn't let you do this because it is smart enough to do control flow analysis to determine the bounds of generic type parameters at any given point of a method. As far as it is concerned, return MySpecializedViewFoo() means the same thing inside and outside the if statement.
The compiler isn't designed like that because you are not supposed to checking the type of a generic type parameter anyway.
You should use two overloads instead:
func createView(for item: Foo) -> MyGenericView<Foo>
func createView(for item: Bar) -> MyGenericView<Bar>
In fact, your checks aren't adequate at all in the first place, so even if the compiler is smart enough, it will tell you that what you are doing isn't safe. Here's an example of how this could go wrong:
class FooSubclass: Foo {}
let foo = FooSubclass1()
let view: MyGenericView<FooSubclass1> = createView(foo)
Using the fix at the beginning, this will crash at runtime. createView will try to create and return a MySpecializedViewFoo, which is not a MyGenericView<FooSubclass1>. Swift generics are invariant. Note that in this case, it will go into the is Foo branch, not the fatalError branch.
Another case is using Base as T:
let foo: Base = Foo()
let view: MyGenericView<Base> = createView(for: foo)
This again will not go into the fatalError branch. If you make Base a protocol, you can make it produce an error when T is inferred to be Base. If you also make Foo and Bar final, then I think that should be safe, from the point of view of the hypothetical "smart" compiler.

Swift, classes based on extended protocol don't conform to original protocol

These protocols are giving me nightmares.
I am trying to implement a couple protocols and classes conforming to them, such that I can have default implementations, but customized implementations are available by extending the protocols/classes. So far, this is what I have:
protocol ProtA {
var aProperty: String { get set }
var anotherProperty:String { get set }
func aFunc (anArgument: String) -> String
}
protocol ProtB: ProtA {
var aThirdProperty: String { get set }
}
protocol ProtC {
func doSomething(parameter: Int, with anotherParameter: ProtA)
}
class ClassA: ProtA {
var aProperty: String = "Hello, I'm a String."
var anotherProperty: String = "I'm not the same String."
func aFunc (anArgument: String) -> String {
return anArgument
}
}
class ClassB: ProtB {
var aProperty: String = "Hello, I'm a String."
var anotherProperty: String = "I'm not the same String."
var aThirdProperty: String = "I'm yet another String!"
func aFunc (anArgument: String) -> String {
return anArgument
}
}
class ClassC: ProtC {
func doSomething(parameter: Int, with anotherParameter: ProtA) {
print (anotherParameter.aProperty) // Works fine.
}
}
Then, if I do
class ClassC: ProtC {
func doSomething(parameter: Int, with anotherParameter: ProtA) {
print (anotherParameter.aProperty) // Works fine.
}
}
But, if I do
class ClassD: ProtC {
func doSomething(parameter: Int, with anotherParameter: ProtA) {
print (anotherParameter.aThirdProperty) // Value of type 'ProtA' has no member 'aThirdProperty'
}
}
and, if instead I do
class ClassE: ProtC {
func doSomething(parameter: Int, with anotherParameter: ProtB) {
print (anotherParameter.aThirdProperty) // Type 'ClassE' does not conform to protocol 'ProtC'
}
}
What am I doing wrong?
The problem
When inheriting from a type, you cannot narrow down the types of the parameters used in overridden functions. This is what you've done by changing the parameter from type ProtA (a more general type), to ProtB (a more specific type).
This is a consequence of the Liskov substitution principle. Simply put, a subclass must be able to do (at minimum) everything that the superclass can do.
ProtC establishes that all conforming types have a function func doSomething(parameter: Int, with anotherParameter: ProtA), with type (Int, ProtA) -> Void).
Your modified function in ClassE has type (Int, ProtB) -> Void. However, this function can no longer act as a substitute for the one it overrides.
Suppose that it was possible to do what you tried. Watch what would happen:
let instanceConformingToProtA: ProtA = ClassA()
let instanceConformingToProtC: ProtC = ClassE()
// This would have to be possible:
instanceConformingToProtC(parameter: 0, amotherParameter: instanceConformingToProtA)
But, ClassE() can't take instanceConformingToProtA as a valid argument to its second parameter, because it's a ProtA, not the required ProtB.
The solution
The solution to this problem is entirely dependant on what you're trying to achieve. I would need further information before being able to proceed.
As a rule of thumb, when overriding inherited members:
Parameter types must be the same, or more general.
E.g. you can't override a function with a parameter of type Car, and change the parameter type to RaceCar. Doing so breaks your classes ability to work with RaceCars, which it must be able to do by the LSP.
E.g. you can override a function with a parameter of type Car, and change the parameter to Vehicle. Doing so preserves your classes' ability to work with `Vehicles.
Return types must be the same, or more specific.
E.g. you can't override a function with a return type of Car with a function that returns Vehicle. Doing so would mean the returned value is "less powerful" than the super class guarantees it should be.
E.g. you can override a function with a return type of Car with a function that returns RaceCar. Doing so would mean the returned value is "more powerful", and it does at least as much as what the super class guarentees.
There's nothing wrong. You should just make sure that the declarations are semantically consistent.
You should either create ProtD declaring the method with a ProtB parameter OR unwrapping the gotten ParamA parameter to use it as ProtB.
func doSomething(parameter: Int, with anotherParameter: ProtB) {
if let a = anotherParameter as? ProtA {
print (a.aThirdProperty)
}
}

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

'Self' cannot be used in non-trivial closures

I would like to have a class with static initialization method:
class A {
required init() {
}
// this one works
class func f0() -> Self {
return self.init()
}
// this one works as well
class func f1() -> Self {
let create = { self.init() } // no error, inferred closure type is '() -> Self'
return create()
}
}
Unfortunately, Swift 3 compiler is unable to infer type for any closure more complex than { self.init() }. For example:
class func f2() -> Self {
let create = {
// error: unable to infer complex closure return type; add explicit type to disambiguate
let a = self.init()
return a
}
return create()
}
Any attempt to specify closure type, variable type explicitly or cast from A to Self leads to an error:
class func f3() -> Self {
let create = { () -> Self in // error: 'Self' is only available in a protocol or as the result of a method in a class;
let a = self.init()
return a
}
return create()
}
class func f4() -> Self {
let create = {
let a: Self = self.init() // error: 'Self' is only available in a protocol or as the result of a method in a class;
return a
}
return create()
}
class func f5() -> Self {
let create = { () -> A in
let a = self.init()
return a
}
return create() as! Self // error: cannot convert return expression of type 'A' to return type 'Self'
}
The solution is to avoid closures using Self.
This seems like a very unfortunate limitation of the compiler. Is there a reason behind it? Is this issue likely to be fixed in future Swift versions?
The fundamental problem is that Swift needs to determine the type of Self at compile time, but you want to determine it at runtime. It has been somewhat expanded to allow class functions to return Self if everything can be resolved at compile time, but Swift can't always prove that your types must be correct in some of these cases (sometimes because it doesn't know how yet, and sometimes because it's actually possible to be wrong).
As an example, consider a subclass that doesn't override a Self-returning method. How can it return the subclass? What if it passes Self to something else? How can you statically type check that at compile time given future subclasses the compiler doesn't even know about? (Including subclasses that can be added at runtime and across module boundaries.)
I expect this to get a little better, but Swift discourages this kind of complex inheritance (these are non-final classes, so you must consider all the possible subclasses) and prefers protocols long-term, so I wouldn't expect this to be fully possible in Swift 4 or 5.

Swift: Return class constrained with generic type

I have a basic generic class:
class SharedClass<T> {}
And a builder for it:
class SharedClassBuilder {
func build<T>() -> SharedClass<T>? {
return ...
}
}
What I want to be able to do, is return an instance that inherits SharedClass, and conforms to T. For example:
protocol MyObject {
func doIt()
}
var result: SharedClass<MyObject>? = SharedClassBuilder().build()
result?.doIt()
Unfortunately, the Swift compiler complains that the returned type does not have a member named doIt.
Is there a way to achieve what I'm looking for?
I suspect it's not so much that you want the returned class to be constrained by the generic type, as you're asking the returned class to be an instance of the constrained type. In your snippet, you're expecting the unwrapped result to conform to MyObject. Taking this a step further, it means that the conformance of SharedClass is determined entirely from how it was constructed. As far as I know this isn't supported in Swift.
However, there's nothing stopping you having a member of SharedClass that is a T. Something along the lines of:
class SharedClass<T> {
var v : T?
}
class SharedClassBuilder {
func build<T>() -> SharedClass<T>? {
return SharedClass()
}
}
protocol MyObject {
func doIt()
}
var result: SharedClass<MyObject>? = SharedClassBuilder().build()
result?.v?.doIt()