[Pitch #2] Function builders

Hi Douglas,

Is there a way to have a debug mode that will output the translated code you showed? or some way to see this intermediate step would be great.

Thank you,
Chéyo

1 Like

I don't think there's any way to get at the actual source of the builder-transformed code (since we don't have a general utility for going backwards from AST to source code), but if you run swiftc -dump-ast file.swift, the generated output is post-typecheck (and so also post-builder-transform). If there's a better way to view the output of the function builder transformation, I'd love to know as well!

@beccadax has a tracing function builder. As for the compiler, if someone went ahead and implemented pretty-printing for expressions, you could use its AST-printing facilities to see what's happening.

Doug

4 Likes

Hey. Didn't read the entire conversation, but the proposal itself. Any reason why declarations are left alone by the transformation? The only reason I can find in the proposal is: "This allows developers to factor out subexpressions freely to clarify their code, without affecting the function builder transformation." Imho it should be left to the DSL in question to decide wether to allow people to factor out declarations. There are enough other ways to factor out code, but I could imagine reasonable usecases and an appropriate buildDeclaration for such a transform...

Edit:

To elaborate: Say, we declare a variable a. What the transformation has to do now is to wrap the entire scope where a is available into a closure and pass a somehow transformed a to that closure. That is, all you need is a

static func buildDeclaration(expr: GivenType,
                             continuation: @escaping (NewType) -> Component) 
-> Component

In the DSL, the declared variable will then be inferred to have type NewType rather than GivenType.

Usecase:

static func buildDeclaration<T,U, E : Error>(expr: Result<T,E>,
                                continuation: (T) -> Result<U,E>)
 -> Result<U,E>{
             expr.flatMap(continuation)
}

and now assuming we use a functionbuilder with the above method to create a constructor for Result itself, the callsite could look like this:

Result(42){int in 
let a = someFuncReturningResultA(int)
let b = someFuncReturningResultB(a)
someFuncReturningResultC(int,a,b)
}//Result<C,Error>

Edit edit:

Of course, you can support multiple such buildDeclaration functions as long as lookup is unique. In above scenario, you may want to have another buildDeclaration like

static func buildDeclaration<T,U, E : Error>(expr: T,
                                             continuation: (T) -> Result<U,E>)
 -> Result<U,E>{
           continuation(expr)
}

which is essentially the default-implementation if there's no type-transformation from expr to the input of continuation, i.e. if we define that declaring a buildDeclaration that does some type transformation just doesn't override the default implementation, we may not even need to write this extra code.

You may have ended up reading outdated documents @Anachron.

This is the "pitch thread", while the feature now known as "result builders" has already been accepted (after revisions): SE-0289 (review #2): Result Builders - #141 by compnerd so refer to that thread what the status quo is.

What you are mentioning with buildDeclaration sounds to me exactly like the feature I mentioned early on when working on some internal DSLs, and it's right now in the "future directions" section:

The full motivation post is here: Function builders and "including" let declarations in built result