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

Howard Lovatt howard.lovatt at gmail.com
Mon Feb 20 18:07:45 CST 2017


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> 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> 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)
>
> Slava
>
> On Feb 16, 2017, at 9:05 AM, Isaac Rivera via swift-users <
> 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
> https://lists.swift.org/mailman/listinfo/swift-users
>
>
>
> _______________________________________________
> swift-users mailing list
> swift-users at swift.org
> https://lists.swift.org/mailman/listinfo/swift-users
>
-- 
-- Howard.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-users/attachments/20170221/534df071/attachment.html>


More information about the swift-users mailing list