Incorrectly Inferred Type in Generic Closure with Protocol Extension Constrained by Self

Michael Fourre

While trying to implement a function in Swift I ran into an issue where it seems that type inference broke on certain function parameters. Taking notes from other languages, I wanted to implement a function in Swift which takes two parameters: an object and a block which returns another object of the same type as the previous object. I started implementing it like so:

func prepare<T>(_ object: T?, with block: ((T?) -> T?)) -> T? {
    return block(object)
}

After attempting to use that inside of a UITableViewController class, I experienced an issue with the naming, as it seemed to be struggling with the segue function prepare(for:sender:)

I decided to just change the name to resolve this one (I'll save the above for another question):

func bootstrap<T>(_ object: T?, with block: ((T?) -> T?)) -> T? {
    return block(object)
}

This name will do for now. So I have a bootstrap(_:with:) function which takes an object and a closure, returning an object of the same type as the object argument. I can call this easily with certain objects, for example an Int:

bootstrap(1, with: { $0 + 1 })  // returns 2

This of course isn't what the function is there for, however. So I used it in its actual intended application, where I have a small UIView-derived class with a single member:

class ContactDetailHeaderView: UIView, Nibbable {
    var user: User?
}

This is fetched from a nib using the following protocol extension function on the Nibbable protocol:

extension Nibbable where Self: UIResponder {
    static func getRoot(withOwner owner: Any, options: [AnyHashable:Any]? = nil) -> Self? {
        return self.nib.instantiate(withOwner: owner, options: options).first as? Self
    }
}

With this, we can retrieve an object from nib (our UINibobject for this class) and cast it as Self which in this case is ContactDetailHeaderView. Then, we can use our previously declared function to setup the object. We do this inside of a UITableViewController-derived class ContactDetailTableViewController which I will condense for the purpose of this question:

class ContactDetailTableViewController: UITableViewController {
    var user: User?

    override func viewDidLoad() {
        super.viewDidLoad()
        self.tableView.backgroundView = bootstrap(ContactDetailHeaderView.getRoot(withOwner: self), with: { $0?.user = self.user })
    }
}

However, we get an error invoking this function:

Value of type 'UIView' has no member 'user'

This is unexpected. Our getRoot(withOwner:options:) function should return Self which is ContactDetailHeaderView, right?

Let's expand on the type inference by replacing the shorthand arguments with named parameters and explicitly defined types:

bootstrap(ContactDetailHeaderView.getRoot(withOwner: self),
    with: { (header: ContactDetailHeaderView?) -> ContactDetailHeaderView? in
        header?.user = self.user
    }
)

Surely there is no way that our object can be interpreted as UIView now? Well, we still get the same error:

Value of type 'UIView' has no member 'user'

Why does this happen? Even when I check the autocomplete in Xcode, the header argument here shows the user member as a valid accessible property. Using the getRoot(withOwner:options:) function elsewhere produces the expected result:

ContactDetailHederView.getRoot(withOwner: self)?.user = self.user

Then why does it not work inside the bootstrap(_:with:) closure? Is this a bug? Is there something else I'm missing?

Any help would be greatly appreciated.

I'll attach a gist with enough code to reproduce the problem in a playground. For simplicity's sake, I'll replace some of the unnecessary bits with something more manageable (e.g. change user type from User which is an internal object to String which is available in the standard library)

Please keep in mind that this question is not about what the function does or why it does it. There are plenty of ways to implement the same thing, many of which are likely far better. I would like to solve this compiler error regardless of whether or not I even use this function in its current form.

Rob Napier

Please keep in mind that this question is not about what the function does or why it does it. There are plenty of ways to implement the same thing, many of which are likely far better. I would like to solve this compiler error regardless of whether or not I even use this function in its current form.

Good, because this has way too many optionals. But it's easy to fix (it's just really hard to understand because it's too complicated).

bootstrap is supposed to return a T?. You pass it this:

{ (header: ContactDetailHeaderView?) -> ContactDetailHeaderView? in
    header?.user = self.user
}

That returns Void. That causes the type engine to run down all kinds of rabbit holes (especially since any T can become T?). The fix is to return T:

{ (header: ContactDetailHeaderView?) -> ContactDetailHeaderView? in
    header?.user = self.user
    return header
}

In your gist, this would be:

self.tableView.backgroundView = bootstrap(ContactDetailHeaderView.getRoot(withOwner: self), 
                                with: { $0?.user = self.user; return $0 })

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

From Dev

Type incorrectly inferred

From Dev

Conforming a generic type to a protocol in a Swift extension

From Dev

Generic closure in protocol

From Dev

Protocol extensions on Structs causes compile error 'Self' constrained to non-protocol type

From Dev

Swift closure in protocol extension

From Dev

Can't omit the extension type argument when calling generic extension method with mutually constrained type parameters

From Dev

Generic parameter 'Element' could not be inferred Swift closure

From Dev

Generic constrained type default value

From Dev

how to have generic constrained type

From Dev

"self as?" within protocol extension

From Dev

Why is a generic type unable to be inferred?

From Dev

Automatically inferred generic type in trait

From Dev

Scala method Inferred generic type

From Dev

Downcast Generic AnyObject to Protocol Associated Type Self.Model

From Dev

Protocol with property of type Self can only be used as generic constraint, why?

From Dev

Extending a Protocol where Self: Generic Type in Swift (Requires Arguments In <...>)

From Dev

Extending a Protocol where Self: Generic Type in Swift (Requires Arguments In <...>)

From Dev

Protocol with property of type Self can only be used as generic constraint, why?

From Dev

Generic parameter 'Self' could not be inferred swift

From Dev

Store a closure with generic type

From Dev

Type 'Error' constrained to non-protocol type even the type is a protocol

From Dev

Inferred type in generic with multiple type parameters

From Dev

Invalid Cast of Type Constrained C# Generic

From Dev

Why can the type not be inferred for this generic Clamp method?

From Dev

Chained generic type inferred closures in swift

From Dev

Protocol function with generic type

From Dev

Using a generic type passed without constraint with a constrained generic type

From Dev

Workaround for conditional conformance in Swift (was: Adding protocol to a constrained generic types)

From Dev

Define generic type for use in closure

Related Related

  1. 1

    Type incorrectly inferred

  2. 2

    Conforming a generic type to a protocol in a Swift extension

  3. 3

    Generic closure in protocol

  4. 4

    Protocol extensions on Structs causes compile error 'Self' constrained to non-protocol type

  5. 5

    Swift closure in protocol extension

  6. 6

    Can't omit the extension type argument when calling generic extension method with mutually constrained type parameters

  7. 7

    Generic parameter 'Element' could not be inferred Swift closure

  8. 8

    Generic constrained type default value

  9. 9

    how to have generic constrained type

  10. 10

    "self as?" within protocol extension

  11. 11

    Why is a generic type unable to be inferred?

  12. 12

    Automatically inferred generic type in trait

  13. 13

    Scala method Inferred generic type

  14. 14

    Downcast Generic AnyObject to Protocol Associated Type Self.Model

  15. 15

    Protocol with property of type Self can only be used as generic constraint, why?

  16. 16

    Extending a Protocol where Self: Generic Type in Swift (Requires Arguments In <...>)

  17. 17

    Extending a Protocol where Self: Generic Type in Swift (Requires Arguments In <...>)

  18. 18

    Protocol with property of type Self can only be used as generic constraint, why?

  19. 19

    Generic parameter 'Self' could not be inferred swift

  20. 20

    Store a closure with generic type

  21. 21

    Type 'Error' constrained to non-protocol type even the type is a protocol

  22. 22

    Inferred type in generic with multiple type parameters

  23. 23

    Invalid Cast of Type Constrained C# Generic

  24. 24

    Why can the type not be inferred for this generic Clamp method?

  25. 25

    Chained generic type inferred closures in swift

  26. 26

    Protocol function with generic type

  27. 27

    Using a generic type passed without constraint with a constrained generic type

  28. 28

    Workaround for conditional conformance in Swift (was: Adding protocol to a constrained generic types)

  29. 29

    Define generic type for use in closure

HotTag

Archive