I'm glad the decision was made to make this a private feature for now. I strongly support the need for DSL s in Swift, but share a lot of others' concerns.
Most of my experience with using and creating many DSLs in the past was with a dynamic language, Groovy, which at least when I was doing it would typically leverage the language's support for closure delegates at runtime, where closures are first-class objects and a delegate
can be set to any instance of anything before calling the closure. There are a lot of issues with that of course, as it is all about dynamic property and method resolution.
I do currently ship a Swift open source framework that has some simple builders using "clunky" builder instances passed to closures (e.g. URL Routes | Flint framework).
Obviously SwiftUI is my only real contact point with this feature and I understand from this thread that SwiftUI has special needs re: generic function signatures and these should be resolved in the fullness of time with variadic generics.
Those current limitations/sharp edges that are present in SwiftUI re: number of expressions capped at 10 and oblique side effects of that (one can imagine View bodies with multiple if statements causing major confusion here in particular)... I personally find in the context of SwiftUI to be really offensive – particularly because of the lack of reasonable error reporting.
This is actually the topic that gives me the greatest concern. I do not think we(*) can add function builders for DSLs as a public feature to Swift until we have a 100% solid strategy for clear and unambiguous error reporting at compile time.
With e.g. a Groovy DSL that is based on property and function access in the current closure scope being resolved on an instance of a "builder" (the closure delegate), while the problems are only found at runtime, they would typically be fairly unambiguous — along the lines of "No function called 'html' found".
As I understand it, being type-based the current proposal can only spit out errors about missing types and builder argument type mismatches which are not at all conducive to regular developers who want to use a DSL created by somebody (like me) as a quick and error-reducing shortcut to achieving something. The moment they spin off into type system errors the language feature has failed them.
If there is no reasonable way for errors in function builder closure bodies to be clearly reported as errors in the context of the DSL (not in the context of the Swift Type system) I will have to say -1 to this kind of proposal and instead advocate for an approach where functions and properties are used instead of types and resolved against an instance of a builder.
The rationale is that autocomplete and basic compiler error reporting works in this case. Problems are manifest in terms of the DSL:
var body: some View {
list {
text("Hello")
thisDoesNotExist()
}
}
If these functions resolve against a builder instance, they can still be verified at compile time, autocomplete suggestions can still work, and missing functions can result in simple errors like:
The ViewBuilder DSL does not have a function thisDoesNotExist() did you meant thisOtherOne()?
I have a very strong fear response that we will end up with impenetrable compiler/type system errors and broken autocomplete suggestions for many years to come and hobble the success of SwiftUI and other frameworks that provide DSLs as a result. It will have a reputation of "Ooooh no we don't use that as nobody can debug it".
Which is almost the very opposite of why DSLs exist.
*: who am I kidding, I don't understand enough of the compiler stuff to contribute)