Functional decomposition with Result Builders

At the moment it doesn't look like it is possible to use functional decomposition with Result Builders. For example with this code

@CodeBlockItemListBuilder func statements() -> ExpressibleAsCodeBlockItemList {
    ImportDecl(path: "Foundation")
    
    ClassDecl(classOrActorKeyword: .class, identifier: "SomeViewController", membersBuilder: {
        VariableDecl(.let, name: "tableView", type: "UITableView")
    })
}

it seems reasonable that the following should be equivalent

@CodeBlockItemListBuilder func statements() -> ExpressibleAsCodeBlockItemList {
    ImportDecl(path: "Foundation")
    
    moreStatements()
}

@CodeBlockItemListBuilder func moreStatements() -> ExpressibleAsCodeBlockItemList {
    ClassDecl(classOrActorKeyword: .class, identifier: "SomeViewController", membersBuilder: {
        VariableDecl(.let, name: "tableView", type: "UITableView")
    })
}

Am I missing something here?

1 Like

You've changed the type: moreStatements returns ExpressibleAsCodeBlockItemList, which isn't a single element of CodeBlockItemList. By analogy, your first example has:

let elements = [ImportDecl(…), ClassDecl(…)]

and your second example has

let extracted = [ClassDecl(…)]
let elements = [ImportDecl(), extracted] // extra level of nesting

You can explicitly have your builder do flattening, or you can have your extracted function return a single code block item instead of a list.

1 Like

So for clarity and prosperity, adding another buildExpression override to the builder will allow this to work-

  /// Provide the ability to an expression that returns an `ExpressibleAsCodeBlockItemList`
  ///  such as another @CodeBlockItemListBuilder
  public static func buildExpression(_ expression: ExpressibleAsCodeBlockItemList) -> Component {
    let codeBlockItemList = expression.createCodeBlockItemList()
    return codeBlockItemList.elements
  }

Makes sense, although not particularly obvious.

2 Likes