Generics and Function Builders

Hi there,
I have a question about Function Builders and Generics:

The TL;DR is:
I have a function builder that is generic with type constraints. The builder simply collects a variadic number of elements of a related generic type into an array.
When I use the function builder, the generic type cannot be inferred even though I would assume that this was possible.

The smallest example I could find that demonstrates the issue is:

public protocol FieldProvider {
    associatedtype Field
    func value(for field: Field) -> String?
}

public enum TemplateItem<F: FieldProvider> {
    case staticText(String)
    case field(F.Field)
}

@_functionBuilder
public class GenericFunctionBuilder<F: FieldProvider> {
    static func buildBlock(_ children: TemplateItem<F>...) -> [TemplateItem<F>] {
        children
    }
}

struct Generic<F: FieldProvider> {
    let items: [TemplateItem<F>]
    public init(items: [TemplateItem<F>]) {
        self.items = items
    }
    public init(@GenericFunctionBuilder<F> content: () -> [TemplateItem<F>]) {
        self.items = content()
    }
}

// Usage

struct Business {
    enum Fields {
        case name
    }
    var name: String
}

extension Business: FieldProvider {
    typealias Field = Fields

    func value(for field: Business.Fields) -> String? {
        switch field {
        case .name:
            return name
        }
    }
}

func createUsingEnum() -> Generic<Business> {
    Generic(items: [
        .staticText("Name"),
        .field(.name)
    ])
}

func create() -> Generic<Business> {
    Generic {
        TemplateItem<Business>.staticText("Name")
        TemplateItem<Business>.field(.name)
    }
}

As can be seen in createUsingEnum() the generic type Business can be inferred.
But in the function builder version create(), the type cannot be inferred and I need to spell out the full generic type.

As far as I can tell, the compiler should have enough information to infer the type.
I have tried building with Swift 5.1, the latest 5.2 snapshot and latest master branch snapshot. All with similar results.

I am curious to hear whether it ought to be possible for the compiler to infer the type - full well knowing that function builders have not yet been officially accepted and added to the language.

Or might there be some logical fallacy in what I am attempting to do?

Note that the example above is abbreviated a bit - with my small proof-of-concept I would be able to use the small DSL like this:

let t: Template<Business> =
    Template {
        Text("Business")
        Field(.name)
        SubField(employees) {
            Text("Name")
            Field(.employeeName)
        }
    }

while with my troubles with automatic inference I have to write:

let t: Template<Business> =
    Template {
        Text("Business") as Template<Business>.Item
        Field(.name) as Template<Business>.Item
        SubField(employees) {
            Text("Name") as Template<Employee>.Item
            Field(.employeeName) as Template<Employee>.Item
        } as Template<Business>.Item
    }
1 Like

I found the discussion about this and it appears that this will be possible in the future! :heart:

https://forums.swift.org/t/function-builders-implementation-progress/32981

Terms of Service

Privacy Policy

Cookie Policy