Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Swift Expression Macros: a practical introduction

Swift Expression Macros: a practical introduction

Swift Expression Macros:
a practical introduction
-実践 Swiftマクロ入門-

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

Kishikawa Katsumi

January 21, 2023
Tweet

More Decks by Kishikawa Katsumi

Other Decks in Programming

Transcript

  1. kishikawa katsumi / @[email protected] Swift Expression Macros: a practical introduction

    -࣮ફ SwiftϚΫϩೖ໳-
  2. 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 ໨࣍
  3. Macro proposal timeline

  4. • 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
  5. • 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
  6. • 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
  7. • 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
  8. એݴϚΫϩʢ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
  9. Swift macro overview

  10. Swift Macro Overview SwiftϚΫϩͷ֓ཁ func f(a: Int, _: Double, c:

    Int) { print(#function) }
  11. func f(a: Int, _: Double, c: Int) { print(#function) //

    => f(a:_:c:) } Swift Macro Overview SwiftϚΫϩͷ֓ཁ
  12. 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:)") }
  13. 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)
  14. 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
  15. 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
  16. 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: _, _, _) ... }
  17. 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: _, _, _)”)
  18. 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
  19. 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
  20. How to implement macros

  21. How to implement macros #colorLiteral() let color = #colorLiteral(red: 0.5,

    green: 0.5, blue: 0.25, alpha: 1.0)
  22. How to implement macros #colorLiteral() @expression public macro colorLiteral( red:

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

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

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

    Float, green: Float, blue: Float, alpha: Float ) -> _ColorLiteralType = #externalMacro(module: "MacroExamplesPlugin", type: "ColorLiteralMacro")
  26. 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 } }
  27. 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 } }
  28. 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 } }
  29. 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 } }
  30. 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 } }
  31. 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 } }
  32. 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 } }
  33. Swift Syntax

  34. 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 }
  35. 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)) ) }
  36. 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)) ) }
  37. 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)) ) }
  38. How to implement macros #printArguments() func doSomething(_ a: Int, b:

    Int, c d: Int, e _: Int, _: Int, _ _: Int) { #printArguments() ... }
  39. 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))\")" } }
  40. 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 ... } }
  41. 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 ... } }
  42. 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 ... } }
  43. 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))\")" } }
  44. Swift macro essentials

  45. Swift macro essentials • Type safe • Syntactic transformations •

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

    Run at build time • Additional checks at build time • Sandboxed
  47. Swift macro essentials if let leadingTrivia = macro.leadingTrivia { return

    initSyntax.withLeadingTrivia(leadingTrivia) } return initSyntax
  48. Swift macro essentials • Type safe • Syntactic transformations •

    Run at build time • Additional checks at build time • Sandboxed
  49. 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
  50. #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 ) ) ) } ... }
  51. #URL() Compile error if argument is an invalid URL

  52. Swift macro essentials • Type safe • Syntactic transformations •

    Run at build time • Additional checks at build time • Sandboxed
  53. Sandboxed Embed binaries in apps without bundles let data =

    #embed("images/icon.png") let data = Data([0x4d, 0x49, 0x54, ..., 0x65, 0x6e, 0x73])
  54. Sandboxed Can I access the fi le system from macros?

    https://forums.swift.org/t/pitch-2-expression-macros/61861/9
  55. Pros and cons

  56. 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
  57. Pros and cons Swift Syntax

  58. Things not suited for macro • Use functions in preference

    to macros • Use Result Builders and Property Wrappers • Macros are code generators Pros and cons
  59. Various kinds of macros

  60. 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)) } }
  61. 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) }
  62. 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)))") }
  63. 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 { }
  64. Practical Example: 
 Power Assert

  65. 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)
  66. 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)
  67. 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()
  68. Why Power Assert? Many di ff erent assertion functions

  69. Why Power Assert? Many di ff erent assertion functions

  70. Why Power Assert? Many di ff erent assertion functions

  71. 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
  72. Why Power Assert? That's all you need @expression public macro

    powerAssert(_ expression: @autoclosure () throws -> Bool)
  73. Implement Power Assert with macros

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

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

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

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

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

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

    TupleExprElementList Identi fi erExpr BinaryOperatorExpr IntegerLiteralExpr Inspect the macro syntax structure
  80. 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()
  81. 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()
  82. 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()
  83. 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()
  84. 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()
  85. 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
  86. 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)
  87. Implement Power Assert with macros #powerAssert(String(data: data, encoding: .utf8) ==

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

    "test") FunctionCallExpr Identi fi erExpr MemberAccessExpr
  89. 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()
  90. 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()
  91. 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
  92. 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
  93. Implement Power Assert with macros https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md#macro-argument-type-information

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

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

  96. 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!
  97. Easy? Difficult? https://forums.swift.org/t/pitch-declaration-macros/62373/21

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

  99. 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!
  100. 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