ra1028/Carbon

Generic parameter 'Element' could not be inferred

wousser opened this issue · 5 comments

Checklist

Expected Behavior

While updating from RC2 to RC4, the next issue happens.

Current Behavior

Array of class [ClassName], using the group function to list as cells.

cells: {
                        Group(of: state.todos.enumerated()) { offset, todo in
                            TodoText(todo: todo, isCompleted: false)
}}

Detailed Description (Include Screenshots)

However, using another class results in this error:
Generic parameter 'Element' could not be inferred. Explicitly specify the generic arguments to fix this issue
When explicitly specifying arguments I get the following error:
Unable to infer closure type in the current context.

What am I doing wrong?

Environment

  • Carbon version:
    RC4

@wousser

The compiler may have output an incorrect error.
Is TodoText conform to Identifiable Component?
TodoText(todo: todo, isCompleted: false).identified(by: \.todo.id)
Isn't there any other parameters wrong?

BTW, thank you for opening some issues.
However, they were the general question related to the Swift type system, so please try-and-error by referring to the API doc before opening the issue.

Thanks for your quick answers.

The reason I'm asking here is that it worked with RC2, I didn't expect any breaking changes in an RC.

I'm trying to rewrite this part:

struct UpcomingEvent {
    typealias ID = UUID

    var id: ID
    var date: Date
    var events: [Any] // Contact, (Reminder, Contact), (Date, Contact)
}

struct State {
  var upcomingEvents = [UpcomingEvent]()
}
Section(id: "upcoming") { section in
                section.header = ViewNode(UpcomingHeader())

                state.upcomingEvents.forEach({ event in
                    section.cells.append(
                        CellNode(UpcomingDateCard(date: event.date))
                    )

                    event.events.forEach({ (eventType) in

                        //contact
                        if let contact = eventType as? Contact {
                            section.cells.append(
                                CellNode(UpcomingContactCellNode(contact: contact))
                            )
                        }

                        //reminder
                        if let reminder = eventType as? (Reminder, Contact) {
                            section.cells.append(
                                CellNode(UpcomingReminderCellNode(reminder: reminder))
                            )
                        }

                        //birthday
                        if let birthday = eventType as? (Date, Contact) {
                            section.cells.append(
                                CellNode(UpcomingBirthday(birthday: birthday))
                            )
                        }
                    })
                })
        }

All CellNode's conform to Identifiable Component.

I tried to rewrite it as Group(of: ) but wasn't able to. Maybe you can share the best way to go ahead?

Dynamic type casting isn't compatible with function builder because if let and switch statements are not allowed.
I recommended to rewrite using enum, but you can also take the following workaround with legacy API.
This hasn't actually try to compiled, but it probably works.

Section(
    id: "upcoming",
    header: ViewNode(UpcomingHeader()),
    cells: state.upcomingEvents.flatMap { event -> [CellNode] in
        event.events.compactMap { eventType -> CellNode? in
            //contact
            if let contact = eventType as? Contact {
                return CellNode(UpcomingContact(contact: contact))
            }

            //reminder
            if let reminder = eventType as? (Reminder, Contact) {
                return CellNode(UpcomingReminder(reminder: reminder))
            }

            //birthday
            if let birthday = eventType as? (Date, Contact) {
                return CellNode(UpcomingBirthday(birthday: birthday))
            }

            return  nil
        }
    }
)

Another approach with function builder.

func component(for eventType: Any) -> AnyComponent? {
    //contact
    if let contact = eventType as? Contact {
        return AnyComponent(UpcomingContactCellNode(contact: contact))
    }

    //reminder
    if let reminder = eventType as? (Reminder, Contact) {
        return AnyComponent(UpcomingReminderCellNode(reminder: reminder))
    }

    //birthday
    if let birthday = eventType as? (Date, Contact) {
        return AnyComponent(UpcomingBirthday(birthday: birthday))
    }

    return  nil
}


Section(
    id: "upcoming",
    header:UpcomingHeader(),
    cells: {
        Group(of: state.upcomingEvents) { event in
            Group(of: event.events.compactMap { eventType in
                component(for: eventType)
            }
        }
    }
)

Thanks for your help. That approach works great.