In order to create that Chain
method independently of the number of components passed to buildBlock
, you need either an homogeneous array, variadic generics or a lift over existentials based on protocols with self or associated types (PATs). Since variadic generics and unlocked PATs aren't yet available, in the case of SwiftUI Views you need to explicitly use AnyView
. As I'm aware, there's no other way unfortunately.
protocol Arrow {
static var identity: Self { get }
func compose(with other: Self) -> Self
}
@resultBuilder
struct ComposeBuilder<Component: Arrow> {
static func buildBlock(_ components: Component...) -> Component {
components.reduce(.identity) { result, component in
result.compose(with: component)
}
}
}
func Compose<Content: Arrow>(
@ComposeBuilder<Content> body: () -> Content
) -> Content {
body()
}
With AnyView
you can explicitly add Arrow
conformance, of course:
extension AnyView: Arrow {
func compose(with other: AnyView) -> AnyView {
return AnyView(TupleView((self, other)))
}
static var identity: AnyView { AnyView(EmptyView()) }
}
Compose {
AnyView(Text("Swift"))
AnyView(Text("Scala"))
AnyView(Text("Haskell"))
}
and use Compose { ... }
with any Arrow
conforming type:
extension Int: Arrow {
func compose(with other: Int) -> Int { self + other }
static var identity: Int { 0 }
}
Compose {
1
2
3
}