$30 off During Our Annual Pro Sale. View Details »

enum で KeyPaths のような機能を実現する CasePaths

Aikawa
September 28, 2022

enum で KeyPaths のような機能を実現する CasePaths

After Party iOSDC Japan 2022 で発表した内容です

Aikawa

September 28, 2022
Tweet

More Decks by Aikawa

Other Decks in Programming

Transcript

  1. enum Ͱ Key Paths ͷΑ͏ͳ 
 ػೳΛ࣮ݱ͢Δ Case Paths After

    Party iOSDC Japan 2022 kalupas226
  2. ࣗݾ঺հ • ΞΠΧϫ • @kalupas226 • Cookpad Inc. iOS Developer

    2
  3. ΞδΣϯμ • Key Paths ͷجຊ • Case Paths ʹ͍ͭͯ •

    Case Paths ͱ͸ʁ • Case Paths ͷجຊͱར༻ྫ • Case Paths ͷ࣮૷໘നϙΠϯτ • Case Paths ͷར༻ʹෆ͕҆͋Δํ΁ • ·ͱΊ 3
  4. Key Paths ͷجຊ

  5. Key Paths ͱ͸ʁ • Swift Ͱ struct Λੜ੒͢ΔͱԿ΋ͤͣʹར༻Ͱ͖Δπʔϧ • struct

    ʹ͓͚Δ struct ࣗ਎ͷܕͱ fi eld ͷܕ৘ใΛදݱͰ͖ΔͨΊ 
 ༷ʑͳ༻్Ͱར༻Ͱ͖Δ • ׳ΕΔ·Ͱ͸ࣗ༝ࣗࡏʹ࢖͏ͷ͕೉͍͔͠΋͠Εͳ͍ • SwiftUI, Combine, KeyPath Member Lookup ͳͲͰ΋ 
 ར༻͞Ε͍ͯΔ 5
  6. Key Paths ͷجຊ struct User { var id: Int var

    name: String var address: Address? } var user = User( id: 42, name: "kalupas", address: .init(street: "ͩ͜͜Α") ) // user[keyPath: \.id] Ͱ΋ OK user[keyPath: \User.id] // 42 user[keyPath: \.id] = 57
  7. Key Paths ͷجຊ struct User { var id: Int var

    name: String var address: Address? } var user = User( id: 42, name: "kalupas", address: .init(street: "ͩ͜͜Α") ) // user[keyPath: \.id] Ͱ΋ OK user[keyPath: \User.id] // 42 user[keyPath: \.id] = 57
  8. Key Paths ͷجຊ struct User { var id: Int var

    name: String var address: Address? } var user = User( id: 42, name: "kalupas", address: .init(street: "ͩ͜͜Α") ) // user[keyPath: \.id] Ͱ΋ OK user[keyPath: \User.id] // 42 user[keyPath: \.id] = 57
  9. Key Paths ͷجຊ struct User { var id: Int var

    name: String var address: Address? } var user = User( id: 42, name: "kalupas", address: .init(street: "ͩ͜͜Α") ) // user[keyPath: \.id] Ͱ΋ OK user[keyPath: \User.id] // 42 user[keyPath: \.id] = 57
  10. ͜Ε͚ͩͩͱ Key Paths ͷࢫຯ͕ͳ͍ user[keyPath: \.id] // 42 user[keyPath: \.id]

    = 57 ! dot syntax Ͱ୅༻Ͱ͖Δ user.id // 42 user.id = 57
  11. Key Paths ͷخ͠͞ΛҰ෦ൈਮͯ͠঺հ • ӳจͷΑ͏ͳߏจʹͰ͖Δ • Ҿ਺ͱͯ͠ getter ͱ setter

    Λඞཁͱ͢ΔγʔϯͰҾ਺Λ 
 ίϯύΫτʹͰ͖Δ • map ͳͲͷߴ֊ؔ਺Ͱ΋ར༻Ͱ͖Δ 11
  12. Key Paths ͷخ͠͞ΛҰ෦ൈਮͯ͠঺հ • ӳจͷΑ͏ͳߏจʹͰ͖Δ • Ҿ਺ͱͯ͠ getter ͱ setter

    Λඞཁͱ͢ΔγʔϯͰҾ਺Λ 
 ίϯύΫτʹͰ͖Δ • map ͳͲͷߴ֊ؔ਺Ͱ΋ར༻Ͱ͖Δ 12
  13. ӳจͷΑ͏ͳߏจʹ / Ҿ਺ΛίϯύΫτʹ Ͱ͖Δ class Label { var text =

    "" var fontSize = 12 } class ProductModel { var name = "kalupas" } let model = ProductModel() let label = Label()
  14. Label ʹ ProductModel ͷ property Λ bind ͢Δ class Label

    { var text = "" var fontSize = 12 } class ProductModel { var name = "kalupas" } let model = ProductModel() let label = Label()
  15. Label ʹ ProductModel ͷ property Λ bind ͢Δ class Label

    { var text = "" var fontSize = 12 } class ProductModel { var name = "kalupas" } let model = ProductModel() let label = Label() bind(model, \.name, to: label, \.text)
  16. Label ʹ ProductModel ͷ property Λ bind ͢Δ class Label

    { var text = "" var fontSize = 12 } class ProductModel { var name = "kalupas" } let model = ProductModel() let label = Label() bind(model, \.name, to: label, \.text) • ӳจͷΑ͏ʹಡΊΔ • Bind model name to label text • getter, setter Λ؆ܿʹهड़Ͱ͖Δ • \.name = KeyPath (getter) • \.text = ReferenceWritableKeyPath (getterɾsetter)
  17. Key Paths ͷخ͠͞ΛҰ෦ൈਮͯ͠঺հ • ӳจͷΑ͏ͳߏจʹͰ͖Δ • Ҿ਺ͱͯ͠ getter ͱ setter

    Λඞཁͱ͢ΔγʔϯͰҾ਺Λ 
 ίϯύΫτʹͰ͖Δ • map ͳͲͷߴ֊ؔ਺Ͱ΋ར༻Ͱ͖Δ 17
  18. map ͳͲͷߴ֊ؔ਺Ͱ΋ར༻Ͱ͖Δ struct User { var name: String } let

    users = [ .init(...), .init(...), // ... ]
  19. map ͳͲͷߴ֊ؔ਺Ͱ΋ར༻Ͱ͖Δ struct User { var name: String } let

    users = [ .init(...), .init(...), // ... ] let userNames = users.map { $0.name }
  20. map ͳͲͷߴ֊ؔ਺Ͱ΋ར༻Ͱ͖Δ struct User { var name: String } let

    users = [ .init(...), .init(...), // ... ] let userNames = users.map(\.name)
  21. map ͳͲͷߴ֊ؔ਺Ͱ΋ར༻Ͱ͖Δ struct User { var name: String } let

    users = [ .init(...), .init(...), // ... ] let userNames = users.map(\.name) 0249-key-path-literal-function- expressions ʹΑ࣮ͬͯݱ͞Εͨ
  22. ΋ͪΖΜଞʹ΋ Key Paths ͕༗༻ͳ৔໘͸͋Δ • ෳࡶͳϓϩύςΟ΁ͷΞΫηε • KeyPath Member Lookup

    Λར༻ͨ͠ܕ҆શͳϓϩύςΟ΁ͷΞΫηε • SwiftUI • ʮ@StateʯΛʮ$user.nameʯͷΑ͏ʹѻ͑Δ • ʮ@Environment(\.dismiss)ʯ • ForEach ͳͲͰΑ͘ݟ͔͚Δʮ\.selfʯ(Identity key path) ͳͲ΋ • ଞʹ΋୔ࢁ͋Δͱࢥ͍·͕͢ɺ঺հ͖͠Εͳ͍ͷͰׂѪ͠·͢ 22
  23. Case Paths ʹ͍ͭͯ

  24. Case Paths ͱ͸ʁ • Point-Free ੡ͷϥΠϒϥϦ • https://github.com/pointfreeco/swift-case-paths • struct

    → Key Paths, enum → Case Paths • enum Ͱ Key Paths ͷΑ͏ͳػೳ͕࢖͑Δ • Key Paths ͸ʮ\ʯͰΞΫηε͢Δ͕ɺCase Paths ͸ʮ/ʯ • ݩʑ͸ Mirror Λ࢖ͬͨ Re fl ection ʹΑΓ࣮૷͞Ε͍͕ͯͨɺݱࡏ͸ Swift ͷ 
 Pointer Λར༻͍ͯ͠ΔͷͰ Mirror ΑΓ͸҆શͳ࣮૷ํ๏ʹมΘ͍ͬͯΔ 24
  25. Case Paths ͷجຊ enum Authentication { case authenticated(accessToken: String) case

    unauthenticated }
  26. Case Paths ͷجຊ enum Authentication { case authenticated(accessToken: String) case

    unauthenticated } /Authentication.authenticated // CasePath<Authentication, String> ʮ/Enum.Caseʯͱ͍͏ߏจͰ
 CasePath<Enum, Case> ΛखʹೖΕΒΕΔ
  27. enum ʹ associated value ΛೖΕ͍ͨ࣌ (௨ৗ) enum Authentication { case

    authenticated(accessToken: String) case unauthenticated } let authentication = Authentication.authenticated( accessToken: "cafebeef" )
  28. enum ʹ associated value ΛೖΕ͍ͨ࣌ (௨ৗ) enum Authentication { case

    authenticated(accessToken: String) case unauthenticated } let authentication = Authentication.authenticated( accessToken: "cafebeef" )
  29. enum ͷ associated value ΛऔΓग़͍ͨ࣌͠ (௨ৗ) enum Authentication { case

    authenticated(accessToken: String) case unauthenticated } let authentication = Authentication.authenticated( accessToken: "cafebeef" ) if case let .authenticated(accessToken) = authentication { // use accessToken }
  30. enum ͷ associated value ΛऔΓग़͍ͨ࣌͠ (௨ৗ) enum Authentication { case

    authenticated(accessToken: String) case unauthenticated } let authentication = Authentication.authenticated( accessToken: "cafebeef" ) if case let .authenticated(accessToken) = authentication { // use accessToken }
  31. Case Paths Λ࢖ͬͯ associated value ΛೖΕΔ enum Authentication { case

    authenticated(accessToken: String) case unauthenticated } let authentication = Authentication.authenticated( accessToken: "cafebeef" )
  32. Case Paths Λ࢖ͬͯ associated value ΛೖΕΔ enum Authentication { case

    authenticated(accessToken: String) case unauthenticated } // let authentication = Authentication.authenticated( // accessToken: "cafebeef" // ) let authentication = (/Authentication.authenticated).embed("cafebeef")
  33. Case Paths Ͱ associated value ΛऔΓग़͢ enum Authentication { case

    authenticated(accessToken: String) case unauthenticated } let authentication = (/Authentication.authenticated).embed(“cafebeef") if case let .authenticated(accessToken) = authentication { // use accessToken }
  34. Case Paths Ͱ associated value ΛऔΓग़͢ enum Authentication { case

    authenticated(accessToken: String) case unauthenticated } let authentication = (/Authentication.authenticated).embed(“cafebeef") // if case let .authenticated(accessToken) = authentication { // // use accessToken // } (/Authentication.authenticated).extract(from: authentication)
  35. Case Paths ͷଞͷػೳ - Composition - \HighScore.user.name // WritableKeyPath<HighScore, String>

    /Result<Authentication, Error>..Authentication.authenticated // CasePath<Result<Authentication, Error>, String>
  36. Case Paths ͷଞͷػೳ - Identity case path - \User.self //

    WritableKeyPath<User, User> /Authentication.self // CasePath<Authentication, Authentication>
  37. Case Paths ͷར༻ํ๏ • Key Paths ͱಉ͘͡ɺgetterɾsetter ͚ͩͷ໾ׂͰ Case Paths

    Λ 
 ར༻ͯ͠΋ࢫຯΛಘΒΕΔέʔε͸গͳ͍ • Caes Paths ͷར༻ྫΛݟ͍ͯ͘͜ͱͰศར͞Λগ͚ͩ͠঺հ͢Δ • map ͳͲͷߴ֊ؔ਺Ͱͷར༻ • swiftui-navigation 37
  38. map ͳͲͷߴ֊ؔ਺Ͱͷར༻ // Key Paths users.map(\User.name) // Case Paths authentications.compactMap(/Authentication.authenticated)

  39. map ͳͲͷߴ֊ؔ਺Ͱͷར༻ // Key Paths users.map(\User.name) // Case Paths authentications.compactMap(/Authentication.authenticated)

    Case Paths ͷॲཧ͸ɺcase ʹ associated value ͕
 ଘࡏ͠ͳ͍৔߹ nil ʹͳΔͨΊɺcompactMap ͳͲΛར༻͢Δ
  40. swiftui-navigation • Point-Free ੡ͷ SwiftUI Navigation ʹ focus ͨ͠ϥΠϒϥϦ •

    https://github.com/pointfreeco/swiftui-navigation • iOSDC Japan 2022 Ͱൃදͨ͠ͷͰৄ͘͠͸ͦͪΒΛࢀর͍ͩ͘͞🙏 • ʮSwiftUI Navigation ͷ͢΂ͯʯ • swiftui-navigation ͷ಺෦Ͱ΋ Case Paths ͕ར༻͞Ε͍ͯΔ 40
  41. swiftui-navigation struct ContentView: View { enum Route { case create

    case draft(Post) } @State var route: Route? var body: some View { BaseView(...) .sheet( unwrapping: $route, case: /Route.draft ) { $draft in EditPostView(post: $draft) } } }
  42. swiftui-navigation struct ContentView: View { enum Route { case create

    case draft(Post) } @State var route: Route? var body: some View { BaseView(...) .sheet( unwrapping: $route, case: /Route.draft ) { $draft in EditPostView(post: $draft) } } }
  43. swiftui-navigation struct ContentView: View { enum Route { case create

    case draft(Post) } @State var route: Route? var body: some View { BaseView(...) .sheet( unwrapping: $route, case: /Route.draft ) { $draft in EditPostView(post: $draft) } } }
  44. swiftui-navigation extension View { func sheet<Enum, Case, Content>( unwrapping enum:

    Binding<Enum?>, case casePath: CasePath<Enum, Case>, @ViewBuilder content: @escaping (Binding<Case>) -> Content ) -> some View where Content: View { self.sheet( isPresented: `enum`.case(casePath).isPresented() ) { Binding( unwrapping: `enum`.case(casePath) ).map(content) } } }
  45. Case Paths ͷ͓͔͛Ͱ enum ͷಛఆͷ case ʹ 
 Ԡͨ͡ॲཧΛهड़Ͱ͖ΔΑ͏ʹͳΔ extension

    View { func sheet<Enum, Case, Content>( unwrapping enum: Binding<Enum?>, case casePath: CasePath<Enum, Case>, @ViewBuilder content: @escaping (Binding<Case>) -> Content ) -> some View where Content: View { self.sheet( isPresented: `enum`.case(casePath).isPresented() ) { Binding( unwrapping: `enum`.case(casePath) ).map(content) } } } Enum ͷಛఆͷ case ͕ active ͳ࣌ʹͷΈ
 දࣔ͞ΕΔ sheet
  46. Case Paths ͷ࣮૷໘നϙΠϯτ • pre fi x, in fi x

    operator • @_disfavoredOverload • Mirror • Pointer 46
  47. Case Paths ͷ࣮૷໘നϙΠϯτ • pre fi x, in fi x

    operator • @_disfavoredOverload • Mirror • Pointer • ࠓ೔ͷൃද·Ͱʹઆ໌Ͱ͖ΔϨϕϧ·Ͱམͱ͠ࠐΊͳ͔ͬͨͷͰɺ 
 ·ͨͷػձʹهࣄʹ͢Δ͔ൃද͠Α͏ͱࢥ͍·͢🙇 47
  48. Case Paths ͷ࣮૷໘നϙΠϯτ • pre fi x, in fi x

    operator • @_disfavoredOverload • Mirror • Pointer 48
  49. pre fi x, in fi x operator • pre fi

    x: ઀಄ࣙ • in fi x: ઀தࣙ • post fi x (઀ඌࣙ) ΋͋Δ͕ɺͦΕ͸ར༻͞Ε͍ͯͳ͍ͷͰׂѪ • pre fi x operator ͷྫ • Key Paths ʹ͓͚Δʮ\User.idʯɺBoolean ͷ൓సʮ!isPresentedʯͳͲ • in fi x operator ͷྫ • ʮ1 + 2ʯʮ2 - 1ʯͳͲ • operator ͸ࣗ࡞͢Δ͜ͱ͕Ͱ͖Δ 49
  50. Case Paths ʹ͓͚Δ pre fi x operatorʮ/ʯͷࣗ࡞ prefix operator /

    public prefix func / <Root, Value>( embed: @escaping (Value) -> Root ) -> CasePath<Root, Value> { .init(embed: embed, extract: extractHelp(embed)) }
  51. Case Paths ʹ͓͚Δ pre fi x operatorʮ/ʯͷࣗ࡞ prefix operator /

    public prefix func / <Root, Value>( embed: @escaping (Value) -> Root ) -> CasePath<Root, Value> { .init(embed: embed, extract: extractHelp(embed)) }
  52. Case Paths ʹ͓͚Δ pre fi x operatorʮ/ʯͷࣗ࡞ prefix operator /

    public prefix func / <Root, Value>( embed: @escaping (Value) -> Root ) -> CasePath<Root, Value> { .init(embed: embed, extract: extractHelp(embed)) }
  53. Case Paths ʹ͓͚Δ pre fi x operatorʮ/ʯͷࣗ࡞ prefix operator /

    public prefix func / <Root, Value>( embed: @escaping (Value) -> Root ) -> CasePath<Root, Value> { .init(embed: embed, extract: extractHelp(embed)) } /Authentication.authenticated
  54. embed: (Value) -> Root ʹ 
 Authentication.authenticated Λ౉ͤΔཧ༝ embed: @escaping

    (Value) -> Root enum Authentication { case authenticated(accessToken: String) case unauthenticated } let auth = Authentication.authenticated // (String) -> Authentication // (String) -> Authentication // (Value) -> Root
  55. embed: (Value) -> Root ʹ 
 Authentication.authenticated Λ౉ͤΔཧ༝ embed: @escaping

    (Value) -> Root enum Authentication { case authenticated(accessToken: String) case unauthenticated } let auth = Authentication.authenticated // (String) -> Authentication // (String) -> Authentication // (Value) -> Root
  56. embed: (Value) -> Root ʹ 
 Authentication.authenticated Λ౉ͤΔཧ༝ embed: @escaping

    (Value) -> Root enum Authentication { case authenticated(accessToken: String) case unauthenticated } let auth = Authentication.authenticated // (String) -> Authentication // (String) -> Authentication // (Value) -> Root
  57. Case Paths ʹ͓͚Δ in fi x operatorʮ..ʯͷࣗ࡞ precedencegroup CasePathCompositionPrecedence {

    associativity: left } infix operator ..: CasePathCompositionPrecedence extension CasePath { public static func .. <AppendedValue>( lhs: CasePath, rhs: @escaping (AppendedValue) -> Value ) -> CasePath<Root, AppendedValue> { lhs.appending(path: /rhs) } }
  58. Case Paths ʹ͓͚Δ in fi x operatorʮ..ʯͷࣗ࡞ precedencegroup CasePathCompositionPrecedence {

    associativity: left } infix operator ..: CasePathCompositionPrecedence extension CasePath { public static func .. <AppendedValue>( lhs: CasePath, rhs: @escaping (AppendedValue) -> Value ) -> CasePath<Root, AppendedValue> { lhs.appending(path: /rhs) } } precedencegroup Ͱ operator ʹ
 ϧʔϧΛ࣋ͨͤΔ͜ͱ͕Ͱ͖Δ associativity: leftͰ
 operator ʹࠨ݁߹ͷ݁߹ੑΛ࣋ͨͤΒΕΔ (1 + 2) + 3 Ͱ͸ͳ͘ 1 + 2 + 3
 Ͱߦ͚ΔΑ͏ʹ͢ΔΠϝʔδ
  59. Case Paths ʹ͓͚Δ in fi x operatorʮ..ʯͷࣗ࡞ precedencegroup CasePathCompositionPrecedence {

    associativity: left } infix operator ..: CasePathCompositionPrecedence extension CasePath { public static func .. <AppendedValue>( lhs: CasePath, rhs: @escaping (AppendedValue) -> Value ) -> CasePath<Root, AppendedValue> { lhs.appending(path: /rhs) } }
  60. Case Paths ʹ͓͚Δ in fi x operatorʮ..ʯͷࣗ࡞ precedencegroup CasePathCompositionPrecedence {

    associativity: left } infix operator ..: CasePathCompositionPrecedence extension CasePath { public static func .. <AppendedValue>( lhs: CasePath, rhs: @escaping (AppendedValue) -> Value ) -> CasePath<Root, AppendedValue> { lhs.appending(path: /rhs) } } /Result<Authentication, Error> .. Authentication.authenticated lhs rhs
  61. Case Paths ͷ࣮૷໘നϙΠϯτ • pre fi x, in fi x

    operator • @_disfavoredOverload • Mirror • Pointer 61
  62. Swift Ͱ͸ function overload ͕ڐ͞Ε͍ͯΔ func sameName() {} func sameName(value:

    Int) {} func sameName() {} func sameName(value: Int = 5) {} func sameName(_ number: Int) {} func sameName<N: Numeric>(_ number: N) {} func sameName(_ number: Int) {} func sameName(_ numbers: Int...) {}
  63. overload ͞Ε͍ͯΔ৔߹ɺΑΓ۩ମతͳ΋ͷ͕ 
 ༏ઌ͞Εͯݺ͹ΕΔ (ެࣜʹ͸ݴٴ͞Ε͍ͯͳ͍) func sameName() {} func sameName(value:

    Int = 5) {} func sameName(_ number: Int) {} func sameName<N: Numeric>(_ number: N) {} func sameName(_ number: Int) {} func sameName(_ numbers: Int...) {} https://forums.swift.org/t/compiler-choosing-between-functions-with-no-parameters-vs-functions-with-default-parameters/23501/2 https://www. fi vestars.blog/articles/disfavoredOverload/
  64. overload ͞Ε͍ͯΔ৔߹ɺΑΓ۩ମతͳ΋ͷ͕ 
 ༏ઌ͞Εͯݺ͹ΕΔ (ެࣜʹ͸ݴٴ͞Ε͍ͯͳ͍) func sameName() {} // sameName()

    Ͱͪ͜Β͕ݺ͹ΕΔ func sameName(value: Int = 5) {} func sameName(_ number: Int) {} // sameName(5) Ͱͪ͜Β͕ݺ͹ΕΔ func sameName<N: Numeric>(_ number: N) {} func sameName(_ number: Int) {} // sameName(5) Ͱͪ͜Β͕ݺ͹ΕΔ func sameName(_ numbers: Int...) {} https://forums.swift.org/t/compiler-choosing-between-functions-with-no-parameters-vs-functions-with-default-parameters/23501/2 https://www. fi vestars.blog/articles/disfavoredOverload/
  65. sameName(value:) Λ༏ઌͯ͠ݺ͹ͤΔํ๏ func sameName() {} // sameName() Ͱͪ͜Β͕ݺ͹ΕΔ func sameName(value:

    Int = 5) {}
  66. sameName(value:) Λ༏ઌͯ͠ݺ͹ͤΔํ๏ @_disfavoredOverload func sameName() {} func sameName(value: Int =

    5) {} // sameName() Ͱͪ͜Β͕ݺ͹ΕΔΑ͏ʹͳΔ
  67. sameName(value:) Λ༏ઌͯ͠ݺ͹ͤΔํ๏ @_disfavoredOverload func sameName() {} func sameName(value: Int =

    5) {} // sameName() Ͱͪ͜Β͕ݺ͹ΕΔΑ͏ʹͳΔ `@_disfavoredOverload` ʹʮ_ (ΞϯμʔείΞ)ʯ͕
 ෇͍͍ͯΔͷ͸ɺSwift Evolution Λ௨ա͍ͯ͠ͳ͍ͨΊɻ Library headers ʹ͸ `@_disfavoredOverload` Ͱ͋Δ͜ͱ͕
 දࣔ͞Εͳ͍ͨΊɺdocument comment Λ͚ͭΔ͜ͱ͕ॏཁ
  68. Case Paths Ͱ΋ @_disfavoredOverload ͕ 
 ར༻͞Ε͍ͯΔ public prefix func

    / <Root, Value>( embed: @escaping (Value) -> Root ) -> CasePath<Root, Value> { .init(embed: embed, extract: extractHelp(embed)) } @_disfavoredOverload public prefix func / <Root, Value>( embed: @escaping (Value) -> Root ) -> (Root) -> Value? { (/embed).extract(from:) }
  69. Case Paths Ͱ΋ @_disfavoredOverload ͕ 
 ར༻͞Ε͍ͯΔ public prefix func

    / <Root, Value>( embed: @escaping (Value) -> Root ) -> CasePath<Root, Value> { .init(embed: embed, extract: extractHelp(embed)) } @_disfavoredOverload public prefix func / <Root, Value>( embed: @escaping (Value) -> Root ) -> (Root) -> Value? { (/embed).extract(from:) } ීஈ `/Authentication.authenticated` ͷΑ͏ʹ
 ར༻ͨ͠৔߹ɺ্ͷ function ͕༏ઌ͞Εͯݺ͹ΕΔ
 
 `compactMap` ͳͲͰར༻ͨ࣌͠ = `(Root) -> Value?` Λඞཁͱ͢Δ࣌ ͷΈͰԼͷ function ͕ݺ͹ΕΔΑ͏ʹͳ͍ͬͯΔ
  70. Case Paths ͷ࣮૷໘നϙΠϯτ • pre fi x, in fi x

    operator • @_disfavoredOverload • Mirror • Pointer 70
  71. Mirror • ݩʑ Case Paths ͷ಺෦࣮૷ʹ͸ Mirror ͕ར༻͞Ε͍ͯͨ • ݱࡏ͸

    Pointer Λར༻ͨ͠ Mirror ΑΓ͸҆શͳ࣮૷ํ๏ʹͳ͍ͬͯΔ • https://github.com/pointfreeco/swift-case-paths/pull/37 • Mirror ͸ Swift Ͱ Re fl ection Λߦ͑Δ API • Re fl ection: ϓϩάϥϜͷதͰͦͷϓϩάϥϜʹؚ·ΕΔܕ΍ 
 ม਺/ϝιουͷ৘ใΛࢀর/ૢ࡞Ͱ͖ΔΑ͏ʹ͢Δ࢓૊Έ • Mirror ͷڍಈ͸อূ͞Ε͍ͯͳ͍ͨΊɺར༻ʹ͸஫ҙ͕ඞཁ (جຊతʹ͸σόοά༻్͘Β͍ Ͱར༻ͨ͠ํ͕ྑ͍) 71
  72. struct Ͱ Mirror Λར༻͢ΔҰྫ struct User { var id: Int

    var name: String var address: Address? } let user = User(id: 1, name: "kalupas", address: .init(street: "street")) func allProperties(_ value: Any) -> [String] { Mirror(reflecting: value).children.compactMap { $0.label } } print(allProperties(user))
  73. struct Ͱ Mirror Λར༻͢ΔҰྫ struct User { var id: Int

    var name: String var address: Address? } let user = User(id: 1, name: "kalupas", address: .init(street: "street")) func allProperties(_ value: Any) -> [String] { Mirror(reflecting: value).children.compactMap { $0.label } } print(allProperties(user))
  74. struct Ͱ Mirror Λར༻͢ΔҰྫ struct User { var id: Int

    var name: String var address: Address? } let user = User(id: 1, name: "kalupas", address: .init(street: "street")) func allProperties(_ value: Any) -> [String] { Mirror(reflecting: value).children.compactMap { $0.label } } print(allProperties(user)) dump(Mirror(reflecting: value).children.first!) ▿ (2 elements) ▿ label: Optional("id") - some: "id" - value: 1
  75. struct Ͱ Mirror Λར༻͢ΔҰྫ struct User { var id: Int

    var name: String var address: Address? } let user = User(id: 1, name: "kalupas", address: .init(street: "street")) func allProperties(_ value: Any) -> [String] { Mirror(reflecting: value).children.compactMap { $0.label } } print(allProperties(user)) // ["id", "name", "address"]
  76. enum Ͱ Mirror Λར༻͢ΔҰྫ enum Authentication { case authenticated(String) case

    unauthenticated } let auth = Authentication.authenticated("deadbeef") let mirror = Mirror(reflecting: auth)
  77. enum Ͱ Mirror Λར༻͢ΔҰྫ enum Authentication { case authenticated(String) case

    unauthenticated } let auth = Authentication.authenticated("deadbeef") let mirror = Mirror(reflecting: auth)
  78. enum Ͱ Mirror Λར༻͢ΔҰྫ enum Authentication { case authenticated(String) case

    unauthenticated } let auth = Authentication.authenticated("deadbeef") let mirror = Mirror(reflecting: auth) dump(mirror.children.first!) ▿ (2 elements) ▿ label: Optional("authenticated") - some: "authenticated" - value: "deadbeef"
  79. Case Paths Ͱͷ Mirror ͷར༻ྫΛݟͯΈΔ enum Authentication { case authenticated(String)

    case unauthenticated } let auth = Authentication.authenticated("deadbeef") extract(case: Authentication.authenticated, from: auth) // Optional(“deadbeef") ͕खʹೖΔ
  80. Case Paths Ͱͷ Mirror ͷར༻ྫΛݟͯΈΔ enum Authentication { case authenticated(String)

    case unauthenticated } let auth = Authentication.authenticated("deadbeef") extract(case: Authentication.authenticated, from: auth) // Optional(“deadbeef") ͕खʹೖΔ ※ ͜ͷ extract function ͸આ໌ͷͨΊͷ΋ͷͰ͋ΓɺCase Paths ʹ͸ଘࡏ͍ͯ͠ͳ͍
  81. Mirror ʹΑΔ extract ͷ࣮૷ํ๏ 
 (આ໌ͷͨΊͷෆ׬શͳ࣮૷ʣ func extract<Root, Value>( case:

    @escaping (Value) -> Root, from root: Root ) -> Value? { let mirror = Mirror(reflecting: root) guard let child = mirror.children.first else { return nil } guard let value = child.value as? Value else { return nil } let newRoot = `case`(value) let newMirror = Mirror(reflecting: newRoot) guard let newChild = newMirror.children.first else { return nil } guard newChild.label == child.label else { return nil } return value }
  82. Mirror ʹΑΔ extract ͷ࣮૷ํ๏ 
 (આ໌ͷͨΊͷෆ׬શͳ࣮૷ʣ func extract<Root, Value>( case:

    @escaping (Value) -> Root, from root: Root ) -> Value? { let mirror = Mirror(reflecting: root) guard let child = mirror.children.first else { return nil } guard let value = child.value as? Value else { return nil } let newRoot = `case`(value) let newMirror = Mirror(reflecting: newRoot) guard let newChild = newMirror.children.first else { return nil } guard newChild.label == child.label else { return nil } return value } let auth = Authentication.authenticated("deadbeef") extract(case: Authentication.authenticated, from: auth) -> Optional(“deadbeef”)
  83. Mirror ʹΑΔ extract ͷ࣮૷ํ๏ 
 (આ໌ͷͨΊͷෆ׬શͳ࣮૷ʣ func extract<Root, Value>( case:

    @escaping (Value) -> Root, from root: Root ) -> Value? { let mirror = Mirror(reflecting: root) guard let child = mirror.children.first else { return nil } guard let value = child.value as? Value else { return nil } let newRoot = `case`(value) let newMirror = Mirror(reflecting: newRoot) guard let newChild = newMirror.children.first else { return nil } guard newChild.label == child.label else { return nil } return value } dump(mirror.children.first!) ▿ (2 elements) ▿ label: Optional("authenticated") - some: "authenticated" - value: "deadbeef" child value
  84. Mirror ʹΑΔ extract ͷ࣮૷ํ๏ 
 (આ໌ͷͨΊͷෆ׬શͳ࣮૷ʣ func extract<Root, Value>( case:

    @escaping (Value) -> Root, from root: Root ) -> Value? { let mirror = Mirror(reflecting: root) guard let child = mirror.children.first else { return nil } guard let value = child.value as? Value else { return nil } let newRoot = `case`(value) let newMirror = Mirror(reflecting: newRoot) guard let newChild = newMirror.children.first else { return nil } guard newChild.label == child.label else { return nil } return value } extract(case: Authentication.authenticated, from: auth) newRoot = Authentication.authenticated("deadbeef")
  85. Mirror ʹΑΔ extract ͷ࣮૷ํ๏ 
 (આ໌ͷͨΊͷෆ׬શͳ࣮૷ʣ func extract<Root, Value>( case:

    @escaping (Value) -> Root, from root: Root ) -> Value? { let mirror = Mirror(reflecting: root) guard let child = mirror.children.first else { return nil } guard let value = child.value as? Value else { return nil } let newRoot = `case`(value) let newMirror = Mirror(reflecting: newRoot) guard let newChild = newMirror.children.first else { return nil } guard newChild.label == child.label else { return nil } return value } label ͕ਖ਼͍͜͠ͱΛνΣοΫ நग़Ͱ͖ͨ value Λฦ͢
  86. ೚ҙͷ enum ͷ associated value Λ 
 நग़Ͱ͖ΔΑ͏ʹͳͬͨ extract(case: Authentication.authenticated,

    from: auth) // AccessToken extract(case: Authentication.authenticated, from: .unauthenticated) // nil extract(case: Result<Int, Error>.success, from: .success(42)) // 42 struct MyError: Error {} extract(case: Result<Int, Error>.failure, from: .failure(MyError())) // MyError extract(case: Example.foo, from: .foo(2)) // 2 extract(case: Example.bar, from: .foo(2)) // nil
  87. Case Paths ͷར༻ʹෆ͕҆͋Δํ΁ • Swift ͕ఏڙͯ͘͠Εͳ͍΋ͷΛఏڙͰ͖ΔΑ͏ʹ͢ΔͨΊʹɺCase Paths ͸ Pointer Λར༻ͯ͠ػೳΛ࣮ݱ͍ͯ͠Δ

    • ໰୊ͳ͘ಈ࡞͢ΔΑ͏ʹɺϕϯνϚʔΫςετ΍ڍಈΛอূ͢ΔͨΊͷϢχοτςετ͕༻ҙ͞Ε͍ͯΔ • ϕϯνϚʔΫςετ: https://github.com/pointfreeco/swift-case-paths/blob/ 7346701ea29da0a85d4403cf3d7a589a58ae3dee/Sources/swift-case-paths-benchmark/main.swift • Ϣχοτςετ: https://github.com/pointfreeco/swift-case-paths/blob/ 7346701ea29da0a85d4403cf3d7a589a58ae3dee/Tests/CasePathsTests/CasePathsTests.swift • ଟ਺ͷར༻࣮੷΋͋Δ • ݴޠʹ૊Έࠐ·ΕΔ͜ͱ͕͔ͳΓظ଴͞Ε͍ͯͯಈ͖΋׆ൃͳ༷ࢠ • https://forums.swift.org/t/discussion-inclusion-of-casepaths-into-the-language/53024 • https://github.com/apple/swift/pull/58940
  88. Case Paths ͷར༻ʹෆ͕҆͋Δํ΁

  89. ·ͱΊ • struct ʹ͓͚Δ Key Paths ͷΑ͏ͳػೳΛ enum Ͱ࣮ݱ͢Δ Case

    Paths ͱ͍͏ϥΠϒϥϦ͕ Point-Free ʹΑͬͯ 
 ఏڙ͞Ε͍ͯΔ • Case Paths ʹ͸༷ʑͳར༻γʔϯ͕͋Δ • map ͳͲͷߴ֊ؔ਺Ͱͷར༻ɺfunction ͷҾ਺ͱͯ͠ಛఆͷ case Λ׆༻͍ͨ͠৔߹ͳͲ • Case Paths ͷ࣮૷Ͱ͸ҎԼͷΑ͏ͳٕज़͕ར༻͞Ε͍ͯΔ • pre fi x, in fi x operator • @_disfavoredOverload • Mirror (ࠓ͸ར༻͞Ε͍ͯͳ͍) • Pointer • Case Paths ͸͔ͬ͠Γςετ͞Ε͍ͯͯར༻࣮੷΋͋Γɺݴޠػೳʹ૊ΈࠐΉͷͨΊͷಈ͖΋ൃੜ͍ͯ͠Δ
  90. ࢀߟ • Case Paths पล • https://github.com/pointfreeco/swift-case-paths • https://github.com/wickwirew/Runtime •

    https://github.com/pointfreeco/swiftui-navigation • https://forums.swift.org/t/discussion-inclusion-of-casepaths-into-the-language/53024 • Mirror • https://developer.apple.com/documentation/swift/mirror • https://qiita.com/bannzai/items/96b912a9db43cefdb728 • Pointer • https://developer.apple.com/documentation/swift/unsafepointer • https://qiita.com/omochimetaru/items/c95e0d36ae7f1b1a9052
  91. ࢀߟ • pre fi x, in fi x operator •

    https://docs.swift.org/swift-book/LanguageGuide/BasicOperators.html • https://docs.swift.org/swift-book/LanguageGuide/AdvancedOperators.html • disfavoredOverload • https://www. fi vestars.blog/articles/disfavoredOverload/ • Key Paths पล • proposals • https://github.com/apple/swift-evolution/blob/main/proposals/0161-key-paths.md • https://github.com/apple/swift-evolution/blob/main/proposals/0227-identity-keypath.md • https://github.com/apple/swift-evolution/blob/master/proposals/0249-key-path-literal-function-expressions.md • https://github.com/apple/swift-evolution/blob/main/proposals/0252-keypath-dynamic-member-lookup.md • https://www.youtube.com/watch?v=-eRhuwAZyCU • https://www.pointfree.co/episodes/ep8-getters-and-key-paths • https://www.pointfree.co/episodes/ep7-setters-and-key-paths
  92. ิ଍ࢿྉ

  93. Key Paths Λ࢖Θͣʹಉ͡දݱΛ͢Δʹ͸ʁ bind(model, \.name, to: label, \.text)

  94. Key Paths Λ࢖Θͣʹಉ͡දݱΛ͢Δʹ͸ʁ bind(model, \.name, to: label, \.text) ! bind(

    model: model, get: { $0.name }, to: label, get: { $0.text }, set: { $0.text = $1 } )
  95. Key Paths Λ࢖Θͣʹಉ͡දݱΛ͢Δʹ͸ʁ bind(model, \.name, to: label, \.text) ! bind(

    model: model, get: { $0.name }, to: label, get: { $0.text }, set: { $0.text = $1 } ) • ӳจͷΑ͏ʹಡΊͳ͍ • getter, setter Λදݱ͢ΔͨΊʹ Ҿ਺͕ଟ͘ͳΔ
  96. bind ͷྫͷ৔߹ɺҎԼͷΑ͏ʹ͸Ͱ͖Δ bind( model: model, get: { $0.name }, to:

    label, get: { $0.text }, set: { $0.text = $1 } ) ! bind(model.name, to: label)
  97. Combine Ͱ΋ࣅͨΑ͏ͳߏจ͕͋Δ let subject = PassthroughSubject<String, Never>() subject.assign(to: \.text, on:

    label)
  98. Combine Ͱ΋ࣅͨΑ͏ͳߏจ͕͋Δ let subject = PassthroughSubject<String, Never>() subject.assign(to: \.text, on:

    label) • ӳจͷΑ͏ʹಡΊΔ • Ҿ਺͕ίϯύΫτʹͳΔ