Dunfey · Hotel WWDC as data, est. 1983
Front desk everything
Years
Topics

2021 Swift

WWDC21 · 47 min · Swift

Write a DSL in Swift using result builders

Some problems are easier to solve by creating a customized programming language, or “domain-specific language.” While creating a DSL traditionally requires writing your own compiler, you can instead use result builders with Swift 5.4 to make your code both easier to read and maintain. We’ll take you through best practices for designing a custom language for Swift: Learn about result builders and trailing closure arguments, explore modifier-style methods and why they work well, and discover how you can extend Swift’s normal language rules to turn Swift into a DSL. To get the most out of this session, it’s helpful (though not necessary) to have some experience writing SwiftUI views. You won’t need to know anything about parser or compiler implementation.

Watch at developer.apple.com ↗

Transcript all transcripts

Code shown on screen · 46 snippets

FavoriteSmoothies view swift · at 3:15 ↗
struct FavoriteSmoothies: View {
    @EnvironmentObject
    private var model: FrutaModel

    var body: some View {
        SmoothieList(smoothies: model.favoriteSmoothies)
            .overlay(
                Group {
                    if model.favoriteSmoothies.isEmpty {
                        Text("Add some smoothies!")
                            .foregroundColor(.secondary)
                            .frame(maxWidth: .infinity,
                                   maxHeight: .infinity) 
                    }
                }
            )
            .navigationTitle("Favorites")
    }
}
FavoriteSmoothies view (hypothetical alternative) swift · at 3:38 ↗
// Hypothetical code--not actually supported by SwiftUI

struct FavoriteSmoothies: View {
    @EnvironmentObject
    private var model: FrutaModel

    var body: some View {
        var list = SmoothieList(smoothies: model.favoriteSmoothies)
        
        let overlay: View
        if model.favoriteSmoothies.isEmpty {
            var text = Text("Add some smoothies!")
            text.foregroundColor = .secondary

            var frame = Frame(subview: text)
            frame.maxWidth = .infinity
            frame.maxHeight = .infinity
            overlay = frame
        } else {
            overlay = EmptyView()
        }
        
        list.addOverlay(overlay)
        list.navigationTitle = "Favorites"
        
        return list
    }
}
FavoriteSmoothies view swift · at 3:59 ↗
struct FavoriteSmoothies: View {
    @EnvironmentObject
    private var model: FrutaModel

    var body: some View {
        SmoothieList(smoothies: model.favoriteSmoothies)
            .overlay(
                Group {
                    if model.favoriteSmoothies.isEmpty {
                        Text("Add some smoothies!")
                            .foregroundColor(.secondary)
                            .frame(maxWidth: .infinity,
                                   maxHeight: .infinity) 
                    }
                }
            )
            .navigationTitle("Favorites")
    }
}
FavoriteSmoothies view swift · at 6:17 ↗
struct FavoriteSmoothies: View {
    @EnvironmentObject
    private var model: FrutaModel

    var body: some View {
        SmoothieList(smoothies: model.favoriteSmoothies)
            .overlay(
                Group {
                    if model.favoriteSmoothies.isEmpty {
                        Text("Add some smoothies!")
                            .foregroundColor(.secondary)
                            .frame(maxWidth: .infinity,
                                   maxHeight: .infinity) 
                    }
                }
            )
            .navigationTitle("Favorites")
    }
}
Simple result builder example swift · at 9:26 ↗
VStack {
    Text("Title").font(.title)
    Text("Contents")
}
Simple result builder example + struct VStack swift · at 9:36 ↗
VStack {
    Text("Title").font(.title)
    Text("Contents")
}


struct VStack<Content: View>: View {
    
    init(@ViewBuilder content: () -> Content) {
        self.content = content()
    }
}
Simple result builder example + struct VStack + trailing closure applied swift · at 9:40 ↗
VStack /* .init(content: */ {
    Text("Title").font(.title)
    Text("Contents")
} /* ) */


struct VStack<Content: View>: View {
    
    init(@ViewBuilder content: () -> Content) {
        self.content = content()
    }
}
Simple result builder example + struct VStack + trailing closure applied + enum ViewBuilder swift · at 9:50 ↗
VStack /* .init(content: */ {
    Text("Title").font(.title)
    Text("Contents")
    /* return // TODO: build results using ‘ViewBuilder’ */
} /* ) */

struct VStack<Content: View>: View {
    
    init(@ViewBuilder content: () -> Content) {
        self.content = content()
    }
}

@resultBuilder enum ViewBuilder {
    static func buildBlock(_: View...) -> some View {  }
}
Simple result builder example + struct VStack + trailing closure applied + enum ViewBuilder + result builder applied swift · at 10:15 ↗
VStack /* .init(content: */ {
    /* let v0 = */ Text("Title").font(.title)
    /* let v1 = */ Text("Contents")
    /* return ViewBuilder.buildBlock(v0, v1) */
} /* ) */

struct VStack<Content: View>: View {
    
    init(@ViewBuilder content: () -> Content) {
        self.content = content()
    }
}

@resultBuilder enum ViewBuilder {
    static func buildBlock(_: View...) -> some View {  }
}
Fruta's smoothie lists, pre-DSL swift · at 14:49 ↗
// Fruta’s Smoothie lists

extension Smoothie {
    static let berryBlue = Smoothie(
        id: "berry-blue",
        title: "Berry Blue",
        description: "Filling and refreshing, this smoothie will fill you with joy!",
        measuredIngredients: [
            MeasuredIngredient(.orange, measurement: Measurement(value: 1.5, unit: .cups)),
            MeasuredIngredient(.blueberry, measurement: Measurement(value: 1, unit: .cups)),
            MeasuredIngredient(.avocado, measurement: Measurement(value: 0.2, unit: .cups))
        ],
        hasFreeRecipe: true
    )

    static let carrotChops = Smoothie()
    static let crazyColada = Smoothie()
    // Plus 12 more…
}

extension Smoothie {
    private static let allSmoothies: [Smoothie] = [
        .berryBlue,
        .carrotChops,
        .crazyColada,
        // Plus 12 more…
    ]

    static func all(includingPaid: Bool = true) -> [Smoothie] {
        if includingPaid {
            return allSmoothies
        }
        
        logger.log("Free smoothies only")
        return allSmoothies.filter { $0.hasFreeRecipe }
    }
}
Fruta's smoothie lists, pre-DSL (hypothetical alternative) swift · at 14:50 ↗
// Fruta’s Smoothie lists (hypothetical alternative)

extension Smoothie {
    static let berryBlue = Smoothie(
        id: "berry-blue",
        title: "Berry Blue",
        description: "Filling and refreshing, this smoothie will fill you with joy!",
        measuredIngredients: [
            MeasuredIngredient(.orange, measurement: Measurement(value: 1.5, unit: .cups)),
            MeasuredIngredient(.blueberry, measurement: Measurement(value: 1, unit: .cups)),
            MeasuredIngredient(.avocado, measurement: Measurement(value: 0.2, unit: .cups))
        ],
        hasFreeRecipe: true
    )

    static let carrotChops = Smoothie()
    static let crazyColada = Smoothie()
    // Plus 12 more…
}

extension Smoothie {
   static func all(includingPaid: Bool = true) -> [Smoothie] {
       var allSmoothies: [Smoothie] = [
            .berryBlue,
            .carrotChops,
        ]
        
        if includingPaid {
            allSmoothies += [
                .crazyColada,
                // Plus more
            ]
        } else {
            logger.log("Free smoothies only")
        }
        
        return allSmoothies
    }
}
Fruta's smoothie lists, pre-DSL swift · at 14:51 ↗
// Fruta’s Smoothie lists

extension Smoothie {
    static let berryBlue = Smoothie(
        id: "berry-blue",
        title: "Berry Blue",
        description: "Filling and refreshing, this smoothie will fill you with joy!",
        measuredIngredients: [
            MeasuredIngredient(.orange, measurement: Measurement(value: 1.5, unit: .cups)),
            MeasuredIngredient(.blueberry, measurement: Measurement(value: 1, unit: .cups)),
            MeasuredIngredient(.avocado, measurement: Measurement(value: 0.2, unit: .cups))
        ],
        hasFreeRecipe: true
    )

    static let carrotChops = Smoothie()
    static let crazyColada = Smoothie()
    // Plus 12 more…
}

extension Smoothie {
    private static let allSmoothies: [Smoothie] = [
        .berryBlue,
        .carrotChops,
        .crazyColada,
        // Plus 12 more…
    ]

    static func all(includingPaid: Bool = true) -> [Smoothie] {
        if includingPaid {
            return allSmoothies
        }
        
        logger.log("Free smoothies only")
        return allSmoothies.filter { $0.hasFreeRecipe }
    }
}
Near-final DSL design swift · at 18:05 ↗
// DSL top-level design

@SmoothieArrayBuilder
static func all(includingPaid: Bool = true) -> [Smoothie] {
    Smoothie(
        // TODO: Change these parameters
        id: "berry-blue",
        title: "Berry Blue",
        description: "Filling and refreshing, this smoothie will fill you with joy!",
        measuredIngredients: [
            Ingredient.orange.measured(with: .cups).scaled(by: 1.5),
            Ingredient.blueberry.measured(with: .cups),
            Ingredient.avocado.measured(with: .cups).scaled(by: 0.2)
        ]
    )
  
    Smoothie()
  
    if includingPaid {
        Smoothie()
       
        Smoothie()
    } else {
        logger.log("Free smoothies only")
    }
}
Possible DSL description/ingredient designs (start) swift · at 19:57 ↗
// Possible DSL description/ingredient designs

Smoothie(
    id: "berry-blue",
    title: "Berry Blue",
    description: "Filling and refreshing, this smoothie will fill you with joy!",
    measuredIngredients: [
        Ingredient.orange.measured(with: .cups).scaled(by: 1.5),
        Ingredient.blueberry.measured(with: .cups),
        Ingredient.avocado.measured(with: .cups).scaled(by: 0.2)
    ]
)
Possible DSL description/ingredient designs (modifiers) swift · at 20:11 ↗
// Possible DSL description/ingredient designs

Smoothie(id: "berry-blue", title: "Berry Blue")
    .description("Filling and refreshing, this smoothie will fill you with joy!")
    .ingredient(Ingredient.orange.measured(with: .cups).scaled(by: 1.5))
    .ingredient(Ingredient.blueberry.measured(with: .cups))
    .ingredient(Ingredient.avocado.measured(with: .cups).scaled(by: 0.2))
Possible DSL description/ingredient designs (all marker types) swift · at 20:25 ↗
// Possible DSL description/ingredient designs

Smoothie {
    ID("berry-blue")
    Title("Berry Blue")
    Description("Filling and refreshing, this smoothie will fill you with joy!")

    Recipe(
        Ingredient.orange.measured(with: .cups).scaled(by: 1.5),
        Ingredient.blueberry.measured(with: .cups),
        Ingredient.avocado.measured(with: .cups).scaled(by: 0.2)
    )
}
Possible DSL description/ingredient designs (some marker types) swift · at 20:36 ↗
// Possible DSL description/ingredient designs

Smoothie(id: "berry-blue", title: "Berry Blue") {
    Description("Filling and refreshing, this smoothie will fill you with joy!")

    Recipe(
        Ingredient.orange.measured(with: .cups).scaled(by: 1.5),
        Ingredient.blueberry.measured(with: .cups),
        Ingredient.avocado.measured(with: .cups).scaled(by: 0.2)
    )
}
Possible DSL description/ingredient designs (no marker types) swift · at 21:13 ↗
// Possible DSL description/ingredient designs

Smoothie(id: "berry-blue", title: "Berry Blue") {
    "Filling and refreshing, this smoothie will fill you with joy!"

    Ingredient.orange.measured(with: .cups).scaled(by: 1.5)
    Ingredient.blueberry.measured(with: .cups)
    Ingredient.avocado.measured(with: .cups).scaled(by: 0.2)
}
Final DSL design swift · at 21:43 ↗
// DSL top-level design

@SmoothieArrayBuilder
static func all(includingPaid: Bool = true) -> [Smoothie] {
    Smoothie(id: "berry-blue", title: "Berry Blue") {
        "Filling and refreshing, this smoothie will fill you with joy!"

        Ingredient.orange.measured(with: .cups).scaled(by: 1.5)
        Ingredient.blueberry.measured(with: .cups)
        Ingredient.avocado.measured(with: .cups).scaled(by: 0.2)
    }

    Smoothie() {  }

    if includingPaid {
        Smoothie() {  }
    } else {
        logger.log("Free smoothies only")
    }
}
Basic SmoothieArrayBuilder swift · at 24:05 ↗
@resultBuilder
enum SmoothieArrayBuilder {
  static func buildBlock(_ components: Smoothie...) -> [Smoothie] {
    return components
  }
}
How ‘buildBlock(…)’ works swift · at 24:39 ↗
// How ‘buildBlock(…)’ works

@SmoothieArrayBuilder
static func all(includingPaid: Bool = true) {
    /* let v0 = */ Smoothie(id: "berry-blue", title: "Berry Blue") {  }

    /* let v1 = */ Smoothie(id: "carrot-chops", title: "Carrot Chops") {  }

    // …more smoothies…

    /* return SmoothieArrayBuilder.buildBlock(v0, v1, …) */
}
Basic SmoothieArrayBuilder swift · at 25:03 ↗
@resultBuilder
enum SmoothieArrayBuilder {
  static func buildBlock(_ components: Smoothie...) -> [Smoothie] {
    return components
  }
}
Smoothie initializer (incomplete) swift · at 25:56 ↗
extension Smoothie {
  init(id: Smoothie.ID, title: String, /* FIXME */ _ makeIngredients: () -> (String, [MeasuredIngredient])) {
    let (description, ingredients) = makeIngredients()
    self.init(id: id, title: title, description: description, measuredIngredients: ingredients)
  }
}
SmoothieArrayBuilder with simple ‘if’ statements (incorrect) swift · at 27:47 ↗
@resultBuilder
enum SmoothieArrayBuilder {
  static func buildOptional(_ component: [Smoothie]?) -> [Smoothie] {
    return component ?? []
  }
  
  static func buildBlock(_ components: Smoothie...) -> [Smoothie] {
    return components
  }
}
How ‘if’ statements work with ‘buildOptional(_:)’ swift · at 28:01 ↗
// How ‘if’ statements work with ‘buildOptional(_:)’

@SmoothieArrayBuilder
static func all(includingPaid: Bool = true) {
    /* let v0 = */ Smoothie(id: "berry-blue", ) {  }
    /* let v1 = */ Smoothie(id: "carrot-chops", ) {  }

    /* let v2: [Smoothie] */
    if includingPaid {
        /* let v2_0 = */ Smoothie(id: "crazy-colada", ) {  }
        /* let v2_1 = */ Smoothie(id: "hulking-lemonade", ) {  }
        /* let v2_block = SmoothieArrayBuilder.buildBlock(v2_0, v2_1)
           v2 = SmoothieArrayBuilder.buildOptional(v2_block) */
    }
    /* else {
        v2 = SmoothieArrayBuilder.buildOptional(nil)
    } */
    
    /* return SmoothieArrayBuilder.buildBlock(v0, v1, v2) */
}
SmoothieArrayBuilder with simple ‘if’ statements (incorrect) swift · at 29:07 ↗
@resultBuilder
enum SmoothieArrayBuilder {
  static func buildOptional(_ component: [Smoothie]?) -> [Smoothie] {
    return component ?? []
  }
  
  static func buildBlock(_ components: Smoothie...) -> [Smoothie] {
    return components
  }
}
Why didn’t our ‘buildOptional(_:)’ work? swift · at 29:28 ↗
// Why didn’t our ‘buildOptional(_:)’ work?

@SmoothieArrayBuilder
static func all(includingPaid: Bool = true) {
    /* let v0 = */ Smoothie(id: "berry-blue", ) {  }
    /* let v1 = */ Smoothie(id: "carrot-chops", ) {  }

    /* let v2: [Smoothie] */
    if includingPaid {
        /* let v2_0 = */ Smoothie(id: "crazy-colada", ) {  }
        /* let v2_1 = */ Smoothie(id: "hulking-lemonade", ) {  }
        /* let v2_block = SmoothieArrayBuilder.buildBlock(v2_0, v2_1)
           v2 = SmoothieArrayBuilder.buildOptional(v2_block) */
    }
    /* else {
        v2 = SmoothieArrayBuilder.buildOptional(nil)
    } */
    
    /* return SmoothieArrayBuilder.buildBlock(v0, v1, v2) */
}
SmoothieArrayBuilder with simple ‘if’ statements (still incorrect) swift · at 29:40 ↗
@resultBuilder
enum SmoothieArrayBuilder {
  static func buildOptional(_ component: [Smoothie]?) -> [Smoothie] {
    return component ?? []
  }
  
  static func buildBlock(_ components: [Smoothie]...) -> [Smoothie] {
    return components.flatMap { $0 }
  }
}
Why didn’t our ‘buildOptional(_:)’ work? swift · at 30:14 ↗
// Why didn’t our ‘buildOptional(_:)’ work?

@SmoothieArrayBuilder
static func all(includingPaid: Bool = true) {
    /* let v0 = */ Smoothie(id: "berry-blue", ) {  }
    /* let v1 = */ Smoothie(id: "carrot-chops", ) {  }

    /* let v2: [Smoothie] */
    if includingPaid {
        /* let v2_0 = */ Smoothie(id: "crazy-colada", ) {  }
        /* let v2_1 = */ Smoothie(id: "hulking-lemonade", ) {  }
        /* let v2_block = SmoothieArrayBuilder.buildBlock(v2_0, v2_1)
           v2 = SmoothieArrayBuilder.buildOptional(v2_block) */
    }
    /* else {
        v2 = SmoothieArrayBuilder.buildOptional(nil)
    } */
    
    /* return SmoothieArrayBuilder.buildBlock(v0, v1, v2) */
}
The ‘buildExpression(_:)’ method swift · at 31:23 ↗
// The ‘buildExpression(_:)’ method

@SmoothieArrayBuilder
static func all(includingPaid: Bool = true) {
    /* let v0 = SmoothieArrayBuilder.buildExpression( */ Smoothie(id: "berry-blue", ) {  } /* ) */
    /* let v1 = SmoothieArrayBuilder.buildExpression( */ Smoothie(id: "carrot-chops", ) {  } /* ) */

    /* let v2: [Smoothie] */
    if includingPaid {
        /* let v2_0 = SmoothieArrayBuilder.buildExpression( */ Smoothie(id: "crazy-colada", ) {  } /* ) */
        /* let v2_1 = SmoothieArrayBuilder.buildExpression( */ Smoothie(id: "hulking-lemonade", ) {  } /* ) */
        /* let v2_block = SmoothieArrayBuilder.buildBlock(v2_0, v2_1)
           v2 = SmoothieArrayBuilder.buildOptional(v2_block) */
    }
    /* else {
        v2 = SmoothieArrayBuilder.buildOptional(nil)
    } */
    
    /* return SmoothieArrayBuilder.buildBlock(v0, v1, v2) */
}
SmoothieArrayBuilder with simple ‘if’ statements (correct) swift · at 31:44 ↗
@resultBuilder
enum SmoothieArrayBuilder {
  static func buildOptional(_ component: [Smoothie]?) -> [Smoothie] {
    return component ?? []
  }
  
  static func buildBlock(_ components: [Smoothie]...) -> [Smoothie] {
    return components.flatMap { $0 }
  }
  
  static func buildExpression(_ expression: Smoothie) -> [Smoothie] {
    return [expression]
  }
}
SmoothieArrayBuilder with ‘if’-‘else’ statements swift · at 32:48 ↗
@resultBuilder
enum SmoothieArrayBuilder {
  static func buildEither(first component: [Smoothie]) -> [Smoothie] {
    return component
  }
  
  static func buildEither(second component: [Smoothie]) -> [Smoothie] {
    return component
  }
  
  static func buildOptional(_ component: [Smoothie]?) -> [Smoothie] {
    return component ?? []
  }
  
  static func buildBlock(_ components: [Smoothie]...) -> [Smoothie] {
    return components.flatMap { $0 }
  }
  
  static func buildExpression(_ expression: Smoothie) -> [Smoothie] {
    return [expression]
  }
}
How ‘if’-‘else’ statements work with ‘buildEither(…)’ swift · at 32:53 ↗
// How ‘if’-‘else’ statements work with ‘buildEither(…)’

@SmoothieArrayBuilder
static func all(includingPaid: Bool = true) -> [Smoothie] {
    /* let v0: [Smoothie] */
    if includingPaid {
        /* let v0_0 = SmoothieArrayBuilder.buildExpression( */ Smoothie() {  } /* ) */
        /* let v0_block = SmoothieArrayBuilder.buildBlock(v0_0)
           v0 = SmoothieArrayBuilder.buildEither(first: v0_block) */
    }
    else {
        /* let v0_0 = SmoothieArrayBuilder.buildExpression( */ logger.log("Only got free smoothies!") /* ) */
        /* let v0_block = SmoothieArrayBuilder.buildBlock(v0_0)
           v0 = SmoothieArrayBuilder.buildEither(second: v0_block) */
    }
    
    /* return SmoothieArrayBuilder.buildBlock(v0) */
}
How more complicated statements work with ‘buildEither(…)’ swift · at 33:37 ↗
// How more complicated statements work with ‘buildEither(…)’

var v0: [Smoothie]
switch userRegion {
case .americas:
    // ...smoothies omitted...
    /* let v0_block = SmoothieArrayBuilder.buildBlock(...parameters omitted...)
       v0 = SmoothieArrayBuilder.buildEither(first:
                SmoothieArrayBuilder.buildEither(first: v0_block)) */
  
case .asiaPacific:
    // ...smoothies omitted...
    /* let v0_block = SmoothieArrayBuilder.buildBlock(…)
       v0 = SmoothieArrayBuilder.buildEither(first:
                SmoothieArrayBuilder.buildEither(second: v0_block)) */
  
case .eastAtlantic:
    // ...smoothies omitted...
    /* let v0_block = SmoothieArrayBuilder.buildBlock(…)
       v0 = SmoothieArrayBuilder.buildEither(second: v0_block) */
}
SmoothieArrayBuilder with ‘if’-‘else’ statements swift · at 34:12 ↗
@resultBuilder
enum SmoothieArrayBuilder {
  static func buildEither(first component: [Smoothie]) -> [Smoothie] {
    return component
  }
  
  static func buildEither(second component: [Smoothie]) -> [Smoothie] {
    return component
  }
  
  static func buildOptional(_ component: [Smoothie]?) -> [Smoothie] {
    return component ?? []
  }
  
  static func buildBlock(_ components: [Smoothie]...) -> [Smoothie] {
    return components.flatMap { $0 }
  }
  
  static func buildExpression(_ expression: Smoothie) -> [Smoothie] {
    return [expression]
  }
}
How ‘if’-‘else’ statements work with ‘buildEither(…)’ swift · at 34:54 ↗
// How ‘if’-‘else’ statements work with ‘buildEither(…)’

@SmoothieArrayBuilder
static func all(includingPaid: Bool = true) -> [Smoothie] {
    /* let v0: [Smoothie] */
    if includingPaid {
        /* let v0_0 = SmoothieArrayBuilder.buildExpression( */ Smoothie() {  } /* ) */
        /* let v0_block = SmoothieArrayBuilder.buildBlock(v0_0)
           v0 = SmoothieArrayBuilder.buildEither(first: v0_block) */
    }
    else {
        /* let v0_0 = SmoothieArrayBuilder.buildExpression( */ logger.log("Only got free smoothies!") /* ) */
        /* let v0_block = SmoothieArrayBuilder.buildBlock(v0_0)
           v0 = SmoothieArrayBuilder.buildEither(second: v0_block) */
    }
  
    /* return SmoothieArrayBuilder.buildBlock(v0) */
}
SmoothieArrayBuilder with support for ‘Void’ results swift · at 35:07 ↗
@resultBuilder
enum SmoothieArrayBuilder {
  static func buildEither(first component: [Smoothie]) -> [Smoothie] {
    return component
  }
  
  static func buildEither(second component: [Smoothie]) -> [Smoothie] {
    return component
  }
  
  static func buildOptional(_ component: [Smoothie]?) -> [Smoothie] {
    return component ?? []
  }
  
  static func buildBlock(_ components: [Smoothie]...) -> [Smoothie] {
    return components.flatMap { $0 }
  }
  
  static func buildExpression(_ expression: Smoothie) -> [Smoothie] {
    return [expression]
  }
  
  static func buildExpression(_ expression: Void) -> [Smoothie] {
    return []
  }
}
Modifier-style methods on Ingredient and MeasuredIngredient swift · at 36:41 ↗
extension Ingredient {
  func measured(with unit: UnitVolume) -> MeasuredIngredient {
    MeasuredIngredient(self, measurement: Measurement(value: 1, unit: unit))
  }
}

extension MeasuredIngredient {
  func scaled(by scale: Double) -> MeasuredIngredient {
    return MeasuredIngredient(ingredient, measurement: measurement * scale)
  }
}
Closures and result builders swift · at 37:32 ↗
// Closures and result builders

@SmoothieArrayBuilder
static func all(includingPaid: Bool = true) -> [Smoothie] {
    /* let v0 = SmoothieArrayBuilder.buildExpression( */ Smoothie() {
        "Filling and refreshing, this smoothie will fill you with joy!"
        
        Ingredient.orange.measured(with: .cups).scaled(by: 1.5)
        Ingredient.blueberry.measured(with: .cups)
        Ingredient.avocado.measured(with: .cups).scaled(by: 0.2)
    } /* ) */

    /* let v1 = SmoothieArrayBuilder.buildExpression( */ Smoothie() {
        "Packed with vitamin A and C, Carrot Chops is a great way to start your day!"
        
        Ingredient.orange.measured(with: .cups).scaled(by: 1.5)
        Ingredient.carrot.measured(with: .cups).scaled(by: 0.5)
        Ingredient.mango.measured(with: .cups).scaled(by: 0.5)
    } /* ) */

    /* return SmoothieArrayBuilder.buildBlock(v0, v1) */
}
Smoothie initializer (final) and SmoothieBuilder (initial) swift · at 39:22 ↗
extension Smoothie {
  init(id: Smoothie.ID, title: String, @SmoothieBuilder _ makeIngredients: () -> (String, [MeasuredIngredient])) {
    let (description, ingredients) = makeIngredients()
    self.init(id: id, title: title, description: description, measuredIngredients: ingredients)
  }
}

@resultBuilder
enum SmoothieBuilder {
  static func buildBlock(_ description: String, components: MeasuredIngredient...) -> (String, [MeasuredIngredient]) {
    return (description, components)
  }
}
Accepting different types swift · at 40:38 ↗
// Accepting different types

Smoothie() /* @SmoothieBuilder */ {
    /* let v0 = */ "Filling and refreshing, this smoothie will fill you with joy!"
    /* let v1 = */ Ingredient.orange.measured(with: .cups).scaled(by: 1.5)
    /* let v2 = */ Ingredient.blueberry.measured(with: .cups)
    /* let v3 = */ Ingredient.avocado.measured(with: .cups).scaled(by: 0.2)
    
    /* return SmoothieBuilder.buildBlock(v0, v1, v2, v3) */
}
Smoothie initializer (final) and SmoothieBuilder (initial) swift · at 41:01 ↗
extension Smoothie {
  init(id: Smoothie.ID, title: String, @SmoothieBuilder _ makeIngredients: () -> (String, [MeasuredIngredient])) {
    let (description, ingredients) = makeIngredients()
    self.init(id: id, title: title, description: description, measuredIngredients: ingredients)
  }
}

@resultBuilder
enum SmoothieBuilder {
  static func buildBlock(_ description: String, components: MeasuredIngredient...) -> (String, [MeasuredIngredient]) {
    return (description, components)
  }
}
SmoothieBuilder without the string swift · at 42:43 ↗
// SmoothieBuilder without the string

Smoothie() /* @SmoothieBuilder */ {
    // "Filling and refreshing, this smoothie will fill you with joy!"
    /* let v0 = */ Ingredient.orange.measured(with: .cups).scaled(by: 1.5)
    /* let v1 = */ Ingredient.blueberry.measured(with: .cups)
    /* let v2 = */ Ingredient.avocado.measured(with: .cups).scaled(by: 0.2)
    
    /* return SmoothieBuilder.buildBlock(v0, v1, v2) */
}

extension SmoothieBuilder {
    static func buildBlock(_ description: String, _ ingredients: ManagedIngredients...) -> (String, [ManagedIngredients]) {  }
}
How Swift improves diagnostics swift · at 43:38 ↗
// How Swift improves diagnostics

func fn0() throws {}
func fn1() rethrows {}
func fn2() {}

func fn3() deinit {}

func fn4() try {}
SmoothieBuilder without the string swift · at 44:30 ↗
// SmoothieBuilder without the string

Smoothie() /* @SmoothieBuilder */ {
    // "Filling and refreshing, this smoothie will fill you with joy!"
    /* let v0 = */ Ingredient.orange.measured(with: .cups).scaled(by: 1.5)
    /* let v1 = */ Ingredient.blueberry.measured(with: .cups)
    /* let v2 = */ Ingredient.avocado.measured(with: .cups).scaled(by: 0.2)
    
    /* return SmoothieBuilder.buildBlock(v0, v1, v2) */
}

extension SmoothieBuilder {
    static func buildBlock(_ description: String, _ ingredients: ManagedIngredients...)
        -> (String, [ManagedIngredients]) {  }

    @available(*, unavailable, message: "missing ‘description’ field")
    static func buildBlock(_ ingredients: ManagedIngredients...)
        -> (String, [ManagedIngredients]) { fatalError() }
}
Smoothie initializer (final) and SmoothieBuilder (with error handling) swift · at 44:55 ↗
extension Smoothie {
  init(id: Smoothie.ID, title: String, @SmoothieBuilder _ makeIngredients: () -> (String, [MeasuredIngredient])) {
    let (description, ingredients) = makeIngredients()
    self.init(id: id, title: title, description: description, measuredIngredients: ingredients)
  }
}

@resultBuilder
enum SmoothieBuilder {
  static func buildBlock(_ description: String, components: MeasuredIngredient...) -> (String, [MeasuredIngredient]) {
    return (description, components)
  }
  
  @available(*, unavailable, message: "first statement of SmoothieBuilder must be its description String")
  static func buildBlock(_ components: MeasuredIngredient...) -> (String, [MeasuredIngredient]) {
    fatalError()
  }
}

Resources