Slide 1

Slide 1 text

kishikawa katsumi / @[email protected] Swift Expression Macros: a practical introduction -࣮ફ SwiftϚΫϩೖ໳-

Slide 2

Slide 2 text

Agenda • Macro proposal timeline • Swift macro overview • How to implement macros • Swift macro essentials • Pros and cons • Various kinds of macros • Declaration macros • Practical Example: Power Assert • Summary ໨࣍

Slide 3

Slide 3 text

Macro proposal timeline

Slide 4

Slide 4 text

• A Possible Vision for Macros in Swift
 https://forums.swift.org/t/a-possible-vision-for-macros-in-swift/60900
 https://gist.github.com/DougGregor/4f3ba5f4eadac474ae62eae836328b71 • Swift project in 2023
 https://www.swift.org/blog/focus-areas-2023/ Macro proposal timeline A Possible Vision for Macros in Swift

Slide 5

Slide 5 text

• A Possible Vision for Macros in Swift
 https://forums.swift.org/t/a-possible-vision-for-macros-in-swift/60900 • Swift project in 2023
 https://www.swift.org/blog/focus-areas-2023/ ➡[Pitch] Expression macros
 https://forums.swift.org/t/pitch-expression-macros/61499 • [Pitch #2] Expression macros
 https://forums.swift.org/t/pitch-2-expression-macros/61861 • SE-0382: Expression Macros
 https://forums.swift.org/t/se-0382-expression-macros/62090 ϚΫϩʢExpression Macros/ࣜϚΫϩʣͷػೳ͕ঝೝ͞ΕΔ·Ͱ Macro proposal timeline

Slide 6

Slide 6 text

• A Possible Vision for Macros in Swift
 https://forums.swift.org/t/a-possible-vision-for-macros-in-swift/60900 • Swift project in 2023
 https://www.swift.org/blog/focus-areas-2023/ • [Pitch] Expression macros
 https://forums.swift.org/t/pitch-expression-macros/61499 ➡[Pitch #2] Expression macros
 https://forums.swift.org/t/pitch-2-expression-macros/61861 • SE-0382: Expression Macros
 https://forums.swift.org/t/se-0382-expression-macros/62090 ϚΫϩʢExpression Macros/ࣜϚΫϩʣͷػೳ͕ঝೝ͞ΕΔ·Ͱ Macro proposal timeline

Slide 7

Slide 7 text

• A Possible Vision for Macros in Swift
 https://forums.swift.org/t/a-possible-vision-for-macros-in-swift/60900 • Swift project in 2023
 https://www.swift.org/blog/focus-areas-2023/ • [Pitch] Expression macros
 https://forums.swift.org/t/pitch-expression-macros/61499 • [Pitch #2] Expression macros
 https://forums.swift.org/t/pitch-2-expression-macros/61861 ➡SE-0382: Expression Macros
 https://forums.swift.org/t/se-0382-expression-macros/62090 ϚΫϩʢExpression Macros/ࣜϚΫϩʣͷػೳ͕ঝೝ͞ΕΔ·Ͱ Macro proposal timeline

Slide 8

Slide 8 text

એݴϚΫϩʢDeclaration Macrosʣ • [Pitch] Declaration macros
 https://forums.swift.org/t/pitch-declaration-macros/62373/17 • Declaration Macros
 https://github.com/DougGregor/swift-evolution/blob/declaration-macros/proposals/nnnn-declaration-macros.md Macro proposal timeline

Slide 9

Slide 9 text

Swift macro overview

Slide 10

Slide 10 text

Swift Macro Overview SwiftϚΫϩͷ֓ཁ func f(a: Int, _: Double, c: Int) { print(#function) }

Slide 11

Slide 11 text

func f(a: Int, _: Double, c: Int) { print(#function) // => f(a:_:c:) } Swift Macro Overview SwiftϚΫϩͷ֓ཁ

Slide 12

Slide 12 text

func f(a: Int, _: Double, c: Int) { print(#function) // => f(a:_:c:) } Swift Macro Overview SwiftϚΫϩͷ֓ཁ func f(a: Int, _: Double, c: Int) { print("f(a:_:c:)") }

Slide 13

Slide 13 text

Swift Macro Overview SwiftϚΫϩͷ֓ཁ let color = #colorLiteral(red: 0.5, green: 0.5, blue: 0.25, alpha: 1.0) let color = .init(_colorLiteralRed: 0.5, green: 0.5, blue: 0.25, alpha: 1.0)

Slide 14

Slide 14 text

func doSomething(_ a: Int, b: Int, c d: Int, e _: Int, _: Int, _ _: Int) { #printArguments() ... } Swift Macro Overview SwiftϚΫϩͷ֓ཁ https://forums.swift.org/t/se-0382-expression-macros/62090/9

Slide 15

Slide 15 text

func doSomething(_ a: Int, b: Int, c d: Int, e _: Int, _: Int, _ _: Int) { #printArguments() // => doSomething(42, b: 256, c: 512, e: _, _, _) ... } Swift Macro Overview SwiftϚΫϩͷ֓ཁ https://forums.swift.org/t/se-0382-expression-macros/62090/9

Slide 16

Slide 16 text

Swift Macro Overview SwiftϚΫϩͷ֓ཁ func doSomething(_ a: Int, b: Int, c d: Int, e _: Int, _: Int, _ _: Int) { print("doSomething(\(a), b: \(b), c: \(d), e: _, _, _)") ... } https://forums.swift.org/t/se-0382-expression-macros/62090/9 func doSomething(_ a: Int, b: Int, c d: Int, e _: Int, _: Int, _ _: Int) { #printArguments() // => doSomething(42, b: 256, c: 512, e: _, _, _) ... }

Slide 17

Slide 17 text

Swift Macro Overview SwiftϚΫϩͷ֓ཁ #function "f(a:_:c:)" #colorLiteral( red: 0.5, green: 0.5, blue: 0.25, alpha: 1.0 ) .init( _colorLiteralRed: 0.5, green: 0.5, blue: 0.25, alpha: 1.0 ) #printArguments() print("doSomething(\(a), b: \(b), c: \(d), e: _, _, _)”)

Slide 18

Slide 18 text

First impressions about macros Pros and cons • The code might be harder to read • Macros are hard to imagine what they do • Incorrect conversions cause unintended behavior • Macros are hard to debug

Slide 19

Slide 19 text

First impressions about macros Pros and cons • The code might be harder to read • Macros are hard to imagine what they do • Incorrect conversions cause unintended behavior • Macros are hard to debug • Macro can eliminate boilerplate

Slide 20

Slide 20 text

How to implement macros

Slide 21

Slide 21 text

How to implement macros #colorLiteral() let color = #colorLiteral(red: 0.5, green: 0.5, blue: 0.25, alpha: 1.0)

Slide 22

Slide 22 text

How to implement macros #colorLiteral() @expression public macro colorLiteral( red: Float, green: Float, blue: Float, alpha: Float ) -> _ColorLiteralType = #externalMacro(module: "MacroExamplesPlugin", type: "ColorLiteralMacro")

Slide 23

Slide 23 text

How to implement macros #colorLiteral() @expression public macro colorLiteral( red: Float, green: Float, blue: Float, alpha: Float ) -> _ColorLiteralType = #externalMacro(module: "MacroExamplesPlugin", type: "ColorLiteralMacro")

Slide 24

Slide 24 text

How to implement macros #colorLiteral() @expression public macro colorLiteral( red: Float, green: Float, blue: Float, alpha: Float ) -> _ColorLiteralType = #externalMacro(module: "MacroExamplesPlugin", type: "ColorLiteralMacro")

Slide 25

Slide 25 text

How to implement macros #colorLiteral() @expression public macro colorLiteral( red: Float, green: Float, blue: Float, alpha: Float ) -> _ColorLiteralType = #externalMacro(module: "MacroExamplesPlugin", type: "ColorLiteralMacro")

Slide 26

Slide 26 text

How to implement macros #colorLiteral() import SwiftSyntax import _SwiftSyntaxMacros public struct ColorLiteralMacro: ExpressionMacro { public static func expansion( of macro: MacroExpansionExprSyntax, in context: inout MacroExpansionContext ) -> ExprSyntax { let argList = replaceFirstLabel( of: macro.argumentList, with: "_colorLiteralRed" ) let initSyntax: ExprSyntax = ".init(\(argList))" if let leadingTrivia = macro.leadingTrivia { return initSyntax.withLeadingTrivia(leadingTrivia) } return initSyntax } }

Slide 27

Slide 27 text

How to implement macros #colorLiteral() import SwiftSyntax import _SwiftSyntaxMacros public struct ColorLiteralMacro: ExpressionMacro { public static func expansion( of macro: MacroExpansionExprSyntax, in context: inout MacroExpansionContext ) -> ExprSyntax { let argList = replaceFirstLabel( of: macro.argumentList, with: "_colorLiteralRed" ) let initSyntax: ExprSyntax = ".init(\(argList))" if let leadingTrivia = macro.leadingTrivia { return initSyntax.withLeadingTrivia(leadingTrivia) } return initSyntax } }

Slide 28

Slide 28 text

How to implement macros #colorLiteral() import SwiftSyntax import _SwiftSyntaxMacros public struct ColorLiteralMacro: ExpressionMacro { public static func expansion( of macro: MacroExpansionExprSyntax, in context: inout MacroExpansionContext ) -> ExprSyntax { let argList = replaceFirstLabel( of: macro.argumentList, with: "_colorLiteralRed" ) let initSyntax: ExprSyntax = ".init(\(argList))" if let leadingTrivia = macro.leadingTrivia { return initSyntax.withLeadingTrivia(leadingTrivia) } return initSyntax } }

Slide 29

Slide 29 text

How to implement macros #colorLiteral() import SwiftSyntax import _SwiftSyntaxMacros public struct ColorLiteralMacro: ExpressionMacro { public static func expansion( of macro: MacroExpansionExprSyntax, in context: inout MacroExpansionContext ) -> ExprSyntax { let argList = replaceFirstLabel( of: macro.argumentList, with: "_colorLiteralRed" ) let initSyntax: ExprSyntax = ".init(\(argList))" if let leadingTrivia = macro.leadingTrivia { return initSyntax.withLeadingTrivia(leadingTrivia) } return initSyntax } }

Slide 30

Slide 30 text

How to implement macros #colorLiteral() import SwiftSyntax import _SwiftSyntaxMacros public struct ColorLiteralMacro: ExpressionMacro { public static func expansion( of macro: MacroExpansionExprSyntax, in context: inout MacroExpansionContext ) -> ExprSyntax { let argList = replaceFirstLabel( of: macro.argumentList, with: "_colorLiteralRed" ) let initSyntax: ExprSyntax = ".init(\(argList))" if let leadingTrivia = macro.leadingTrivia { return initSyntax.withLeadingTrivia(leadingTrivia) } return initSyntax } }

Slide 31

Slide 31 text

How to implement macros #colorLiteral() import SwiftSyntax import _SwiftSyntaxMacros public struct ColorLiteralMacro: ExpressionMacro { public static func expansion( of macro: MacroExpansionExprSyntax, in context: inout MacroExpansionContext ) -> ExprSyntax { let argList = replaceFirstLabel( of: macro.argumentList, with: "_colorLiteralRed" ) let initSyntax: ExprSyntax = ".init(\(argList))" if let leadingTrivia = macro.leadingTrivia { return initSyntax.withLeadingTrivia(leadingTrivia) } return initSyntax } }

Slide 32

Slide 32 text

How to implement macros #colorLiteral() import SwiftSyntax import _SwiftSyntaxMacros public struct ColorLiteralMacro: ExpressionMacro { public static func expansion( of macro: MacroExpansionExprSyntax, in context: inout MacroExpansionContext ) -> ExprSyntax { let argList = replaceFirstLabel( of: macro.argumentList, with: "_colorLiteralRed" ) let initSyntax: ExprSyntax = ".init(\(argList))" if let leadingTrivia = macro.leadingTrivia { return initSyntax.withLeadingTrivia(leadingTrivia) } return initSyntax } }

Slide 33

Slide 33 text

Swift Syntax

Slide 34

Slide 34 text

Swift Syntax public static func expansion( of macro: MacroExpansionExprSyntax, in context: inout MacroExpansionContext ) -> ExprSyntax { let argList = replaceFirstLabel( of: macro.argumentList, with: "_colorLiteralRed" ) let initSyntax: ExprSyntax = ".init(\(argList))" if let leadingTrivia = macro.leadingTrivia { return initSyntax.withLeadingTrivia(leadingTrivia) } return initSyntax }

Slide 35

Slide 35 text

How to implement macros #colorLiteral() /// Replace the label of the fi rst element in the tuple with the given /// new label. private func replaceFirstLabel( of tuple: TupleExprElementListSyntax, with newLabel: String ) -> TupleExprElementListSyntax { guard let firstElement = tuple.first else { return tuple } return tuple.replacing( childAt: 0, with: firstElement.withLabel(.identifier(newLabel)) ) }

Slide 36

Slide 36 text

How to implement macros #colorLiteral() /// Replace the label of the fi rst element in the tuple with the given /// new label. private func replaceFirstLabel( of tuple: TupleExprElementListSyntax, with newLabel: String ) -> TupleExprElementListSyntax { guard let firstElement = tuple.first else { return tuple } return tuple.replacing( childAt: 0, with: firstElement.withLabel(.identifier(newLabel)) ) }

Slide 37

Slide 37 text

How to implement macros #colorLiteral() /// Replace the label of the fi rst element in the tuple with the given /// new label. private func replaceFirstLabel( of tuple: TupleExprElementListSyntax, with newLabel: String ) -> TupleExprElementListSyntax { guard let firstElement = tuple.first else { return tuple } return tuple.replacing( childAt: 0, with: firstElement.withLabel(.identifier(newLabel)) ) }

Slide 38

Slide 38 text

How to implement macros #printArguments() func doSomething(_ a: Int, b: Int, c d: Int, e _: Int, _: Int, _ _: Int) { #printArguments() ... }

Slide 39

Slide 39 text

How to implement macros #printArguments() public struct PrintArgumentsMacro: ExpressionMacro { public static func expansion( of node: MacroExpansionExprSyntax, in context: inout MacroExpansionContext ) -> ExprSyntax { var syntax = node.as(Syntax.self) while syntax != nil && syntax!.as(FunctionDeclSyntax.self) == nil { syntax = syntax!.parent } guard let functionSyntax = syntax!.as(FunctionDeclSyntax.self) else { return "" } let signature: FunctionSignatureSyntax = functionSyntax.signature let parameterList = signature.input.parameterList let parameters = parameterList.map { parameter -> String in let potentialLabel = parameter.firstName!.withoutTrivia().description let label = potentialLabel == "_" ? nil : potentialLabel let potentialName = parameter.secondName?.withoutTrivia().description ?? potentialLabel let name = potentialName == "_" ? nil : potentialName var string: String if let label { string = "\(label): " } else { string = "" } if let name { string += "\\(\(name))" } else { string += "_" } return string } let parametersString = parameters.joined(separator: ", ") return "print(\"\(raw: functionSyntax.identifier.description)(\(raw: parametersString))\")" } }

Slide 40

Slide 40 text

How to implement macros #printArguments() public struct PrintArgumentsMacro: ExpressionMacro { public static func expansion( of node: MacroExpansionExprSyntax, in context: inout MacroExpansionContext ) -> ExprSyntax { var syntax = node.as(Syntax.self) while syntax != nil && syntax!.as(FunctionDeclSyntax.self) == nil { syntax = syntax!.parent } guard let functionSyntax = syntax!.as(FunctionDeclSyntax.self) else { return "" } let signature: FunctionSignatureSyntax = functionSyntax.signature let parameterList = signature.input.parameterList ... } }

Slide 41

Slide 41 text

How to implement macros #printArguments() public struct PrintArgumentsMacro: ExpressionMacro { public static func expansion( of node: MacroExpansionExprSyntax, in context: inout MacroExpansionContext ) -> ExprSyntax { var syntax = node.as(Syntax.self) while syntax != nil && syntax!.as(FunctionDeclSyntax.self) == nil { syntax = syntax!.parent } guard let functionSyntax = syntax!.as(FunctionDeclSyntax.self) else { return "" } let signature: FunctionSignatureSyntax = functionSyntax.signature let parameterList = signature.input.parameterList ... } }

Slide 42

Slide 42 text

How to implement macros #printArguments() public struct PrintArgumentsMacro: ExpressionMacro { public static func expansion( of node: MacroExpansionExprSyntax, in context: inout MacroExpansionContext ) -> ExprSyntax { var syntax = node.as(Syntax.self) while syntax != nil && syntax!.as(FunctionDeclSyntax.self) == nil { syntax = syntax!.parent } guard let functionSyntax = syntax!.as(FunctionDeclSyntax.self) else { return "" } let signature: FunctionSignatureSyntax = functionSyntax.signature let parameterList = signature.input.parameterList ... } }

Slide 43

Slide 43 text

public struct PrintArgumentsMacro: ExpressionMacro { public static func expansion( of node: MacroExpansionExprSyntax, in context: inout MacroExpansionContext ) -> ExprSyntax { ... let parameters = parameterList.map { parameter -> String in let potentialLabel = parameter.firstName!.withoutTrivia().description let label = potentialLabel == "_" ? nil : potentialLabel let potentialName = parameter.secondName?.withoutTrivia().description ?? potentialLabel let name = potentialName == "_" ? nil : potentialName var string: String if let label { string = "\(label): " } else { string = "" } if let name { string += "\\(\(name))" } else { string += "_" } return string } let parametersString = parameters.joined(separator: ", ") return "print(\"\(raw: functionSyntax.identifier.description)(\(raw: parametersString))\")" } }

Slide 44

Slide 44 text

Swift macro essentials

Slide 45

Slide 45 text

Swift macro essentials • Type safe • Syntactic transformations • Run at build time • Additional checks at build time • Sandboxed

Slide 46

Slide 46 text

Swift macro essentials • Type safe • Syntactic transformations • Run at build time • Additional checks at build time • Sandboxed

Slide 47

Slide 47 text

Swift macro essentials if let leadingTrivia = macro.leadingTrivia { return initSyntax.withLeadingTrivia(leadingTrivia) } return initSyntax

Slide 48

Slide 48 text

Swift macro essentials • Type safe • Syntactic transformations • Run at build time • Additional checks at build time • Sandboxed

Slide 49

Slide 49 text

Additional checks at build time let homepage = #url("https://tryswift.jp/") @expression public macro url(_ string: String) -> URL = ... @expression public macro sql(_ string: String) -> SQLQuery = ... let query = #sql("SELECT * FROM ...") More ideas

Slide 50

Slide 50 text

#URL() Compile error if argument is an invalid URL public static func expansion( of macro: MacroExpansionExprSyntax, in context: inout MacroExpansionContext ) throws -> ExprSyntax { ... if !validate(macro.argumentList.first) { context.diagnose( Diagnostic( node: Syntax(macro), message: SimpleDiagnosticMessage( message: "...", diagnosticID: MessageID(domain: "...", id: "..."), severity: .error ) ) ) } ... }

Slide 51

Slide 51 text

#URL() Compile error if argument is an invalid URL

Slide 52

Slide 52 text

Swift macro essentials • Type safe • Syntactic transformations • Run at build time • Additional checks at build time • Sandboxed

Slide 53

Slide 53 text

Sandboxed Embed binaries in apps without bundles let data = #embed("images/icon.png") let data = Data([0x4d, 0x49, 0x54, ..., 0x65, 0x6e, 0x73])

Slide 54

Slide 54 text

Sandboxed Can I access the fi le system from macros? https://forums.swift.org/t/pitch-2-expression-macros/61861/9

Slide 55

Slide 55 text

Pros and cons

Slide 56

Slide 56 text

Pros and cons 👍 Macros are type safe 👍 Macros can extend Swift 👍 Macros can cooperate with the build process 🤔 Shoot yourself in the foot with any language • Macros are hard to imagine what they do • Macros are hard to debug • Macros or Functions / Property Wrappers / Result Builders 🤔 Requires knowledge of Swift Syntax 🤔 Things not suited for macro

Slide 57

Slide 57 text

Pros and cons Swift Syntax

Slide 58

Slide 58 text

Things not suited for macro • Use functions in preference to macros • Use Result Builders and Property Wrappers • Macros are code generators Pros and cons

Slide 59

Slide 59 text

Various kinds of macros

Slide 60

Slide 60 text

Various kinds of macros Declaration macros (Freestanding macros, Attached macros) @addCompletionHandler func fetchAvatar(_ username: String) async -> Image? { ... } @declaration(peers: [.overloaded]) macro addCompletionHandler: Void /// Expansion of the macro produces the following. func fetchAvatar(_ username: String, completionHandler: @escaping (Image?) -> Void) { Task.detached { completionHandler(await fetchAvatar(username)) } }

Slide 61

Slide 61 text

Various kinds of macros Declaration macros @optionSetMembers struct MyOptions: OptionSet { enum Option: Int { case a case b case c } } // Expands to... struct MyOptions: OptionSet { enum Option: Int { case a case b case c } // Synthesized code below... var rawValue: Int = 0 static var a = MyOptions(rawValue: 1 << Option.a.rawValue) static var b = MyOptions(rawValue: 1 << Option.b.rawValue) static var c = MyOptions(rawValue: 1 << Option.c.rawValue) }

Slide 62

Slide 62 text

Various kinds of macros Declaration macros @traced(logLevel: 2) func myFunction(a: Int, b: Int) { ... } @declaration(body) macro traced(logLevel: Int = 0) if shouldLog(atLevel: 2) { log("Entering myFunction((a: \(a), b: \(b)))") }

Slide 63

Slide 63 text

Various kinds of macros Declaration macros • Replace code generators with a macro @swiftCodeGen enum Asset { enum Images { static let banana = ImageAsset(value: "Exotic/Banana") static let mango = ImageAsset(value: "Exotic/Mango") } enum Colors { static let primary = ColorAsset(value: "Vengo/Primary") static let tint = ColorAsset(value: "Vengo/Tint") } enum Symbols { static let exclamationMark = SymbolAsset(name: "Exclamation Mark") static let plus = SymbolAsset(name: "Plus") } } @swiftCodeGen enum Asset { }

Slide 64

Slide 64 text

Practical Example: 
 Power Assert

Slide 65

Slide 65 text

What is Power Assert? let john = Person(name: "John", age: 42) let mike = Person(name: "Mike", age: 13) #powerAssert(mike.isTeenager && john.age < mike.age)

Slide 66

Slide 66 text

What is Power Assert? #powerAssert(mike.isTeenager && john.age < mike.age) | | | | | | | | | false | | 42 | | 13 | | | | Person(name: "Mike", age: 13) | | | false | | Person(name: "John", age: 42) | false Person(name: "Mike", age: 13) let john = Person(name: "John", age: 42) let mike = Person(name: "Mike", age: 13) #powerAssert(mike.isTeenager && john.age < mike.age)

Slide 67

Slide 67 text

Why Power Assert? Many di ff erent assertion functions public func XCTAssert() public func XCTAssertEqual() public func XCTAssertEqualWithAccuracy() public func XCTAssertFalse() public func XCTAssertGreaterThan() public func XCTAssertGreaterThanOrEqual() public func XCTAssertIdentical() public func XCTAssertLessThan() public func XCTAssertLessThanOrEqual() public func XCTAssertNil() public func XCTAssertNoThrow() public func XCTAssertNotEqual() public func XCTAssertNotEqualWithAccuracy() public func XCTAssertNotIdentical() public func XCTAssertNotNil() public func XCTAssertThrowsError() public func XCTAssertTrue()

Slide 68

Slide 68 text

Why Power Assert? Many di ff erent assertion functions

Slide 69

Slide 69 text

Why Power Assert? Many di ff erent assertion functions

Slide 70

Slide 70 text

Why Power Assert? Many di ff erent assertion functions

Slide 71

Slide 71 text

What is Power Assert? • Only simple assert function such as powerAssert(a == b) is su ff i cient • Display easy-to-understand information on assert failure (when the test does not pass) • Bene fi t of not having to use many di ff erent assertion functions https://azu.github.io/slide/sakurajs/power-assert.html#/2

Slide 72

Slide 72 text

Why Power Assert? That's all you need @expression public macro powerAssert(_ expression: @autoclosure () throws -> Bool)

Slide 73

Slide 73 text

Implement Power Assert with macros

Slide 74

Slide 74 text

Implement Power Assert with macros #powerAssert(a * b == 91)

Slide 75

Slide 75 text

Implement Power Assert with macros https://swift-ast-explorer.com/ Swift AST Explorer

Slide 76

Slide 76 text

Implement Power Assert with macros #powerAssert(a * b == 91) TupleExprElementList Inspect the macro syntax structure

Slide 77

Slide 77 text

Implement Power Assert with macros #powerAssert(a * b == 91) TupleExprElementList Identi fi erExpr Inspect the macro syntax structure

Slide 78

Slide 78 text

Implement Power Assert with macros #powerAssert(a * b == 91) TupleExprElementList Identi fi erExpr BinaryOperatorExpr Inspect the macro syntax structure

Slide 79

Slide 79 text

Implement Power Assert with macros #powerAssert(a * b == 91) TupleExprElementList Identi fi erExpr BinaryOperatorExpr IntegerLiteralExpr Inspect the macro syntax structure

Slide 80

Slide 80 text

Implement Power Assert with macros PowerAssert.Assertion(#"#powerAssert(a * b == 91)"#, line: 2) .assert(a * b == 91) .capture(expression: a.self, column: 13) .capture(expression: b.self, column: 17) .capture(expression: a * b == 91, column: 19) .capture(expression: 91, column: 22) .render()

Slide 81

Slide 81 text

Implement Power Assert with macros PowerAssert.Assertion(#"#powerAssert(a * b == 91)"#, line: 2) .assert(a * b == 91) .capture(expression: a.self, column: 13) .capture(expression: b.self, column: 17) .capture(expression: a * b == 91, column: 19) .capture(expression: 91, column: 22) .render()

Slide 82

Slide 82 text

Implement Power Assert with macros PowerAssert.Assertion(#"#powerAssert(a * b == 91)"#, line: 2) .assert(a * b == 91) .capture(expression: a.self, column: 13) .capture(expression: b.self, column: 17) .capture(expression: a * b == 91, column: 19) .capture(expression: 91, column: 22) .render()

Slide 83

Slide 83 text

Implement Power Assert with macros PowerAssert.Assertion(#"#powerAssert(a * b == 91)"#, line: 2) .assert(a * b == 91) .capture(expression: a.self, column: 13) .capture(expression: b.self, column: 17) .capture(expression: a * b == 91, column: 19) .capture(expression: 91, column: 22) .render()

Slide 84

Slide 84 text

Implement Power Assert with macros PowerAssert.Assertion(#"#powerAssert(a * b == 91)"#, line: 2) .assert(a * b == 91) .capture(expression: a.self, column: 13) .capture(expression: b.self, column: 17) .capture(expression: a * b == 91, column: 19) .capture(expression: 91, column: 22) .render()

Slide 85

Slide 85 text

Implement Power Assert with macros PowerAssert.Assertion(#"#powerAssert(a * b == 91)"#, line: 2) .assert(a * b == 91) .capture(expression: a.self, column: 13) .capture(expression: b.self, column: 17) .capture(expression: a * b == 91, column: 19) .capture(expression: 91, column: 22) .render() // => #powerAssert(a * b == 91) | | | | 10 9 | 91 false

Slide 86

Slide 86 text

Implement Power Assert with macros PowerAssert.Assertion(#"#powerAssert(a * b == 91)"#, line: 2) .assert(a * b == 91) .capture(expression: a.self, column: 13) .capture(expression: b.self, column: 17) .capture(expression: a * b == 91, column: 19) .capture(expression: 91, column: 22) .render() // => #powerAssert(a * b == 91) | | | | 10 9 | 91 false #powerAssert(a * b == 91)

Slide 87

Slide 87 text

Implement Power Assert with macros #powerAssert(String(data: data, encoding: .utf8) == "test") FunctionCallExpr Identi fi erExpr MemberAccessExpr

Slide 88

Slide 88 text

Implement Power Assert with macros #powerAssert(String(data: data, encoding: .utf8) == "test") FunctionCallExpr Identi fi erExpr MemberAccessExpr

Slide 89

Slide 89 text

Implement Power Assert with macros PowerAssert.Assertion(#"#powerAssert(String(data: data, encoding: .utf8) == "test")"#, line: 2) .assert(String(data: data, encoding: .utf8) == "test") .capture(expression: String(data: data, encoding: .utf8), column: 13) .capture(expression: data.self, column: 26) .capture(expression: .utf8, column: 42) .capture(expression: String(data: data, encoding: .utf8) == "test", column: 49) .capture(expression: "test", column: 52) .render()

Slide 90

Slide 90 text

Implement Power Assert with macros PowerAssert.Assertion(#"#powerAssert(String(data: data, encoding: .utf8) == "test")"#, line: 2) .assert(String(data: data, encoding: .utf8) == "test") .capture(expression: String(data: data, encoding: .utf8), column: 13) .capture(expression: data.self, column: 26) .capture(expression: .utf8, column: 42) .capture(expression: String(data: data, encoding: .utf8) == "test", column: 49) .capture(expression: "test", column: 52) .render()

Slide 91

Slide 91 text

Implement Power Assert with macros .capture(expression: data.self, column: 26) .capture(expression: .utf8, column: 42) .capture(expression: String(data: data, No type information in syntax tree

Slide 92

Slide 92 text

Implement Power Assert with macros https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md#macro-argument-type-information No type information in syntax tree

Slide 93

Slide 93 text

Implement Power Assert with macros https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md#macro-argument-type-information

Slide 94

Slide 94 text

Implement Power Assert with macros https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md#example-expression-macros

Slide 95

Slide 95 text

https://github.com/kishikawakatsumi/swift-power-assert Implement Power Assert with macros

Slide 96

Slide 96 text

Summary Macros are fun! • Macros are a very powerful feature that can extend Swift itself • Shoot yourself in the foot if used incorrectly • Swift Syntax becomes more important • Macros are hard to debug • Post your ideas!

Slide 97

Slide 97 text

Easy? Difficult? https://forums.swift.org/t/pitch-declaration-macros/62373/21

Slide 98

Slide 98 text

Easy? Difficult? https://forums.swift.org/t/pitch-declaration-macros/62373/25

Slide 99

Slide 99 text

Summary Macros are fun! • Macros are a very powerful feature that can extend Swift itself • Shoot yourself in the foot if used incorrectly • Swift Syntax becomes more important • Macros are hard to debug • Post your ideas!

Slide 100

Slide 100 text

References • A Possible Vision for Macros in Swift
 https://forums.swift.org/t/a-possible-vision-for-macros-in-swift/60900 • Swift project in 2023
 https://www.swift.org/blog/focus-areas-2023/ • [Pitch] Expression macros
 https://forums.swift.org/t/pitch-expression-macros/61499 • [Pitch #2] Expression macros
 https://forums.swift.org/t/pitch-2-expression-macros/61861 • SE-0382: Expression Macros
 https://forums.swift.org/t/se-0382-expression-macros/62090 • [Pitch] Declaration macros
 https://forums.swift.org/t/pitch-declaration-macros/62373/36 • 5෼͙Β͍ͰΘ͔Δpower assert
 https://azu.github.io/slide/sakurajs/power-assert.html