SE-0289: Function Builders

To add on to this, a search for "swift builder" on Google turns up lots of articles about using the builder design pattern in Swift. Choosing the @builder name would, in the best case, hide these otherwise relevant results. In the worst case, it would make searching for information about the @builder attribute quite difficult. OTOH, searching for "swift result builder" or "swift return value builder" already returns results that almost exclusively discuss "function builders." I don't get the sense that choosing either of these names would 'step on the toes' of existing concepts.

An interesting middle ground is searching for "swift value builder", which returns results which (at least for myself) are approximately 50/50 split between "function builders" and "builder design pattern." I worry that this is the realm we would end up in if we chose a name like @builder.

FWIW, I support the @resultBuilder or @returnValueBuilder namings over the alternatives in this thread (including @functionBuilder) with no strong preference between the two.

2 Likes

I don't agree with this last statement. Conceptually, a given declaration can satisfy the requirements from more than one protocol. In practice, it's just one almost all of the time, and "Jump to Protocol Requirement" will have a single destination. One can handle multiple destinations in a UI in a straightforward manner.

I'm not quite sure what you're looking for here. I wrote some clarifications to buildEither and buildOptional in this clarification PR. Does that align with your suggestion?

Doug

Yeah, I actually reread it and was about to bring it up in that response (and forgot to do so). So the current design have 4 scenarios?

  • No Optional or Either: No branching allowed,
  • Optional only: Only if without else allowed,
  • Either only: Only exhaustive if-else and switch allowed,
  • Both Optional and Either: All if-else and switch allowed.

If so, yes, it does align. Thank you for your patience.

4 Likes

They're discussed a little bit in Future Directions. This virtualized form is fairly different from the design of this feature today. It needs to be a separate thing.

I think we can consider that ship to have sailed, yes.

Doug

1 Like

Yes.

Doug

2 Likes

:thinking::thinking:. That would be true. I suppose my worried is more about whether the IDEs will catch up.

So far they haven't. For instance @dynamicMemberLookup has no links to the definition of the feature, no fix its for the requires, and no autocomplete for the required methods, even in Xcode 12. Perhaps the addition of a far more complex attribute in @*Builder will help prioritize these features.

1 Like

While I don't disagree that the authoring experience could certainly be better (and will be: see below), the bar we've generally set for the evolution process is that the feature can be implemented to provide a good user experience, and the implementation provided is suitable to get a feel for the feature and demonstrate its implementability. I think this proposal clears that bar handily.

You are applying a much more stringent standard on the quality of implementation here. While perhaps that's fine when the proposal authors are core team members and technical leads on the compiler, I would not want this to be the standard to which we hold every proposal. The implementation-required bar is high already, and raising it further in this regard seems unfair to the proposers, especially when some of the requests (e.g., regarding Xcode's behavior) could not possibly be met by anyone outside of Apple.

That's the meta-point. To the point about function builders specifically, yes, they could be better. I've implemented improved code completion and Fix-Its to guide function builder type authors. To save everyone a click, here's what it does:

  • When the function builder transformation cannot be applied because of a missing build method, e.g., because the body uses if-else but the function builder doesn't provide buildEither(first:) and buildEither(second:), add a note + Fix-It to the error message that adds stubs for the functions needed to support those statements. Same for missing buildBlock on a @_functionBuilder type.

  • When code-completing with the definition of a function builder type, provide a full set of completions for all of the build functions that function builders support, along with brief documentation comments stating what each does.

  • Add an educational note outlining the builder functions that a function builder can provide.

    Doug

25 Likes

As to the naming...

Like naming libdispatch "Grand Central Dispatch", I think this should be named:

@magicBuilder

As omochimetaru describes, this is really entering a meta-language, and out-of-Swift experience. Like Author C. Clarke said:

Any sufficiently advanced technology is indistinguishable from magic.

When reading Swift code, when you enter a part marked with @magicBuilder you should not only be prepared for something like custom operators, but a completely different mindset. Like magic. It's a builder using some kind of magic compared to the standard Swift rules.

5 Likes

metaBuilder

1 Like

@magicBuilder sounds like a great name. It's consistent with the UnsafePointer style naming. Where the words Unsafe or Magic act as warnings.

2 Likes

@magicFunctionBuilder at a minimum (IMO), it needs to be clear that this is magic happening within a function context, lest more 'magic' is added in the future. I'm in full agreement that this needs to be very clear this is not a 'normal' feature in Swift.

I am not sure if that would be a good name. Besides, we already have places where “magic” happens, like synthesised conformances, but we don’t use the word “magic” in such contexts. For example, we have a protocol called “Codable” and not “MagicCodable”.

3 Likes

It's amazing to me how much of this review process has been devoted to bikeshedding the property wrapper name.

That is all. Carry on.

2 Likes

The functionality of the proposal is largely locked so there isn't much to discuss there. What there was has already been handled upthread. So between that and the underlying complexity of this feature significantly limiting the number of community members who can really make substantive suggestions means we're going to see a focus on things the rest of the community can handle.

7 Likes

FWIW, the review ends a week ago. Not that that ever stops serious ideas from being expressed.

2 Likes

I've just spoken to a "crack marketing team", and they've suggested @embeddedDSL, which may be a term of art.

6 Likes

I know the review was over a week ago but I forgot I never did a full review.

What is your evaluation of the proposal?

+0.75. I don't really have the expertise to evaluate whether this feature truly meets the desire to enable easy DSLs in Swift, but it seems to work. I still worry about clarity and tooling though.

Is the problem being addressed significant enough to warrant a change to Swift?

I don't think the lack of a nicer DSL syntax is necessarily a problem with Swift, but it's clear there's a desire to make DSL usage easier. This isn't something I would prioritize normally, but with the rise of SwiftUI it obviously make sense to standardize it sooner rather than later.

Does this proposal fit well with the feel and direction of Swift?

This is what I worry about the most. There are three things I that I think could be improved:

  1. Feature Name: As discussed up thread, @functionBuilder doesn't really tell developers when they would use this feature or what it does. I won't rehash the naming discussion from upthread other than to say @dslBuilder gives a strong indication when this feature would be used, but I think @valueBuilder does a better idea of describing what it does.
  2. Tooling: I think this has also been dealt with by now but it's extremely important for the compiler to produce good error message and fixits for developers building a DSL using this feature. Other attribute-based features don't do a great job of this so it'd be great to see some general improvements here.
  3. Usage Site Syntax: This change creates a special syntax form that is, on the surface, indistinguishable from normal Swift usage. I think there should be some sort of visual indication that a closure is a builder closure instead of normal one so users know when they're in a context with the special language behavior. I suggest @, so it would look like @{ }. For the sake of source compatibility this could be optional for now and become required at some point in the (far) future. That would allow developer control over the feature's visibility and for formatters to automatically add or remove the indicators.

If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

I only used the dynamic Ruby DSLs which, AFAIK, aren't similar to this feature.

How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

Reviewed the proposal but didn't get a chance to build an actual DSL, so my understanding is limited.

3 Likes

I feel like making declarative program feel Swifty is a goal for the feature, so this seems like it’s working as intended.

Ok, I'm going to throw another name into the ring:

@aggregator

I feel that this conveys all of the meaning of what is actually happening, and I don't think the term is "taken" or could be confused as anything else. The @aggregator aggregates a series of inputs (usually of the aggregate type) into an aggregated output (of the aggregate type).

So, consider this example:

    var body: some View {
        VStack {
            Text("Hello, world!")
            EmptyView()
        }
    }

In the example, the aggregator is the body, which is an @ViewBuilder (possibly renamed to @ViewAggregator), it aggregates the inputs (VStack, Text, EmptyView) into an aggregated type (some View) according to the rules defined by the aggregator.

4 Likes