[swift-users] The case of broken polymorphism or "Cannot convert value of type to expected argument type"?

Slava Pestov spestov at apple.com
Tue Feb 21 03:39:54 CST 2017


> On Feb 20, 2017, at 4:17 PM, Jon Shier <jon at jonshier.com> wrote:
> 
> Also possibly related is the covariance in protocol requirements. The following example doesn’t compile without casting the arrays or single values to the exact types required in the protocols, despite being covariant through protocol conformance or subclass.
> 
> protocol HasViews {
>     
>     var views: [UIView]!
>     
> }
> 
> protocol Updateable { }
> 
> extension UIView: Updateable { }
> 
> protocol HasProtocols {
>     
>     var updateables: [Updateable]!
>     
> }
> 
> class ViewController: UIViewController {
>     
>     @IBOutlet var views: [UIButton]!
>     @IBOutlet var updateables: [UIButton]!
>     
> }
> 
> extension ViewController: HasViews { } // fails without casting
> extension ViewController: HasProtocols { } // fails without casting

This kind of thing we do want to support eventually, I think.

Method overrides support a limited form of variance — you can make method parameters more optional in an override, or change their type to a base class; similarly a method return type can becomes less optional or change to a subclass. Overrides do not support converting to a protocol existential though.

Protocol requirements do not support variance at all, except for @objc protocols, which allow the same optionality changes as method overrides (but no class upcasts!)

This is all quite confusing to remember, and it would be great to see a proposal that makes the rules more general and consistent between method overrides in classes and protocol requirement witnesses.

Slava

> 
> 
> 
> Jon
> 
> 
>> On Feb 20, 2017, at 7:07 PM, Howard Lovatt via swift-users <swift-users at swift.org <mailto:swift-users at swift.org>> wrote:
>> 
>> It is confusing in Swift what can be covariant and what is invariant, consider:
>> 
>>     // Covarant arrays work
>>     class A {}
>>     class B: A {}
>>     let a = A() // A
>>     let b = B() // B
>>     var arrayA = [a] // Array<A>
>>     arrayA[0] = b // OK
>>     
>>     // And arrays of arrays
>>     var arrayArrayA = [arrayA] // Array<Array<A>>
>>     arrayArrayA[0][0] = b // OK
>>     let arrayB = [b] // Array<B>
>>     arrayArrayA[0] = arrayB // OK, works out that an Array<B> is a Array<A>
>>     
>>     // Covariant homebrew-collections work
>>     class C<T: AnyObject> { 
>>         var e: T
>>         init(_ e: T) { self.e = e }
>>     }
>>     var cA = C(a) // C<A>
>>     cA.e = b // OK
>>     
>>     // But not quite for homebrew-collections of homebrew-collections
>>     var cCA = C(cA) // C<C<A>>
>>     cCA.e.e = b // OK
>>     let cB = C(b) // C<B>
>>     // cCA.e = cB // Error - cannot work out that a C<B> is a C<A> but can do so for arrays
>> 
>> It is weird that the last line fails and the equivalent Array line doesn't. I suspect that there is some special typing going on for arrays, probably to make them play nice with Objective-C. However it would be simpler if everything was covariant when safe to be covariant, i.e. The last line should work.
>> 
>>  -- Howard.
>> 
>> On Mon, 20 Feb 2017 at 8:38 pm, Isaac Rivera via swift-users <swift-users at swift.org <mailto:swift-users at swift.org>> wrote:
>> I can see it is a (counter-intuitive) language design decision for type safety… but then why, in the code below I can:
>> 
>> class OtherThing: Something<UIViewController> {
>> 	override func start(_ completion: SomeCallback? = nil) {
>> 		// implementation details...
>> 	}
>> }
>> 
>> let firstThing = OtherThing(viewController: UINavigationController())
>> 
>> OtherThing extends Something<UIViewController>… but I can instantiate it with the subtype…
>> 
>> Ok you will say, UINavigationController is a subtype of UIViewController, but that still does not make Something<UINavigationController> a subtype of Something<UIViewController>. 
>> 
>> Fair enough, but:
>> 
>> let c1: Something<UIViewController> = Something(viewController: UINavigationController())
>> // c1 is of type "Something<UIViewController>"
>> 
>> let c2 = Something(viewController: UINavigationController())
>> // c1 is of type "Something<UINavigationController>”
>> 
>> So it appears Something<UINavigationController> can be cast to type Something<UIViewController>…
>> 
>> Yet this is illegal?
>> 
>> let somethings: [Something<UIViewController>] = [c1, c2]
>> 
>> I dont know, something seems inconsistent.
>> 
>> 
>>> On Feb 16, 2017, at 10:59 PM, Slava Pestov <spestov at apple.com <mailto:spestov at apple.com>> wrote:
>>> 
>>> Hi Isaac,
>>> 
>>> This is not about associated types. Rather, the issue is that a ‘Thing’ is a ‘Something<UINavigationController>’, but you are casting it to ‘Something<UIViewController>’. The two types are not related; in general, if A is a subtype of B, then G<A> is not a subtype of G<B>.
>>> 
>>> https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science) <https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)>
>>> 
>>> Slava
>>> 
>>>> On Feb 16, 2017, at 9:05 AM, Isaac Rivera via swift-users <swift-users at swift.org <mailto:swift-users at swift.org>> wrote:
>>>> 
>>>> Hello, list!
>>>> 
>>>> I am trying to find my way around Swift’s protocol limitations. It appears that in general, any protocol with declared associatedtype will break polymorphism?
>>>> 
>>>> Take the case below which does not compile. All "Thing” instances are "Something<VC: UIViewController>” but they can’t be passed around or coerced as so.
>>>> 
>>>> How is it that I can legally write the code:
>>>> 
>>>> class Thing: Something<UINavigationController> { }
>>>> 
>>>> and instantiate it, but it is not the very thing it implements? 
>>>> 
>>>> All Thing instances conform to the public interfaces of Something<UIViewController> so why can’t they be recognized as such and coerced as such?
>>>> 
>>>> What is the work-around of this break in Polymorphism?
>>>> 
>>>> import UIKit
>>>> 
>>>> protocol Anything: class, NSObjectProtocol {
>>>> 	
>>>> 	associatedtype ViewControllerType: UIViewController
>>>> 	
>>>> 	var viewController: ViewControllerType { get }
>>>> 	
>>>> 	init(viewController: ViewControllerType)
>>>> 	
>>>> 	func addAnything(anything: Something<UIViewController>) -> Bool
>>>> }
>>>> 
>>>> class Something<VC: UIViewController>: NSObject, Anything {
>>>> 	
>>>> 	typealias ViewControllerType = VC
>>>> 	
>>>> 	private(set) var viewController: ViewControllerType
>>>> 	
>>>> 	required init(viewController: ViewControllerType) { self.viewController = viewController }
>>>> 	
>>>> 	final private var things = [String: Something<UIViewController>]()
>>>> 	
>>>> 	final internal func addAnything(anything: Something<UIViewController>) -> Bool {
>>>> 		// implementation details...
>>>> 		return true
>>>> 	}
>>>> }
>>>> 
>>>> class Thing: Something<UINavigationController> { }
>>>> 
>>>> let firstThing = Thing(viewController: UINavigationController())
>>>> let secondThing = Thing(viewController: UINavigationController())
>>>> 
>>>> firstThing.addAnything(anything: secondThing)
>>>> 
>>>> // Playground execution failed: error: MyPlayground.playground:48:34: error: cannot convert value of type 'Thing' to expected argument type 'Something<UIViewController>'
>>>> 
>>>> firstThing.addAnything(anything: secondThing as Something<UIViewController>)
>>>> 
>>>> // Playground execution failed: error: MyPlayground.playground:48:34: error: cannot convert value of type 'Thing' to type 'Something<UIViewController>' in coercion
>>>> 
>>>> 
>>>> 
>>>> 
>>>> _______________________________________________
>>>> swift-users mailing list
>>>> swift-users at swift.org <mailto:swift-users at swift.org>
>>>> https://lists.swift.org/mailman/listinfo/swift-users <https://lists.swift.org/mailman/listinfo/swift-users>
>>> 
>> 
>> _______________________________________________
>> swift-users mailing list
>> swift-users at swift.org <mailto:swift-users at swift.org>
>> https://lists.swift.org/mailman/listinfo/swift-users <https://lists.swift.org/mailman/listinfo/swift-users>
>> -- 
>> -- Howard.
>> _______________________________________________
>> swift-users mailing list
>> swift-users at swift.org <mailto:swift-users at swift.org>
>> https://lists.swift.org/mailman/listinfo/swift-users
> 

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-users/attachments/20170221/5fd70a89/attachment.html>


More information about the swift-users mailing list