When I read the ResultBuilder on Apple Books Swift 5.5, I am confused with the descrpiton
“For example, if you write a switch statement that has three cases, the compiler uses a binary tree with three leaf nodes. Likewise, because the path from the root node to the second case is “second child” and then “first child”, that case becomes a nested call like buildEither(first: buildEither(second: ... )). The following declarations are equivalent:”
“let someNumber = 19
@ArrayBuilder var builderConditional: [Int] {
if someNumber < 12 {
31
} else if someNumber == 19 {
32
} else {
33
}
}
var manualConditional: [Int]
if someNumber < 12 {
let partialResult = ArrayBuilder.buildExpression(31)
let outerPartialResult = ArrayBuilder.buildEither(first: partialResult)
manualConditional = ArrayBuilder.buildEither(first: outerPartialResult)
} else if someNumber == 19 {
let partialResult = ArrayBuilder.buildExpression(32)
let outerPartialResult = ArrayBuilder.buildEither(second: partialResult)
manualConditional = ArrayBuilder.buildEither(first: outerPartialResult)
} else {
let partialResult = ArrayBuilder.buildExpression(33)
manualConditional = ArrayBuilder.buildEither(second: partialResult)
}”
I have two questions:
What's a binary tree been descrbied looks like?
Why the second case go through second and then first?
For this particular example, the tree would look something like this
- first :
- first: 31
- second: 32
- second: 33
That is why it uses
build(first: build(first: 31)),
build(first: (build(second: 32))), and
build(second:: 33).
The reason for this nesting is because some builders require to identify which particular branch the result comes from. This requirement is lost on ArrayBuilder, unfortunately. If you keep track of the branching, you can see that you got 31 from first -> first branch of the expression, 32 from first -> second, etc.
IIRC, the exact algorithm of the tree is intentionally left unspecified. So the compilers can decide on a more optimal tree structure as needed.
Yes, I did several tests, different branches result in different paths.
I found some useful information in swift/BuilderTransform.cpp at main · apple/swift · GitHub. It should tell the algorithm, but I do not quite understand it.
You aren't supposed to know/rely on the exact shape of the tree. (Technically, you can from the structure of the transformed type.) This allows the compiler to change the tree structuring algorithm as needed. However, it should be enough to distinguish and identify between any two branches from the left-right (first-second) patterns.
That said, the code above makes a balanced tree with residuals put on the left side. Pretty much like a heap (the data structure).