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

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

    View Slide

  2. ࣗݾ঺հ
    • ΞΠΧϫ


    • @kalupas226


    • Cookpad Inc. iOS Developer
    2

    View Slide

  3. ΞδΣϯμ
    • Key Paths ͷجຊ


    • Case Paths ʹ͍ͭͯ


    • Case Paths ͱ͸ʁ


    • Case Paths ͷجຊͱར༻ྫ


    • Case Paths ͷ࣮૷໘നϙΠϯτ


    • Case Paths ͷར༻ʹෆ͕҆͋Δํ΁


    • ·ͱΊ
    3

    View Slide

  4. Key Paths ͷجຊ

    View Slide

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


    • struct ʹ͓͚Δ struct ࣗ਎ͷܕͱ
    fi
    eld ͷܕ৘ใΛදݱͰ͖ΔͨΊ

    ༷ʑͳ༻్Ͱར༻Ͱ͖Δ


    • ׳ΕΔ·Ͱ͸ࣗ༝ࣗࡏʹ࢖͏ͷ͕೉͍͔͠΋͠Εͳ͍


    • SwiftUI, Combine, KeyPath Member Lookup ͳͲͰ΋

    ར༻͞Ε͍ͯΔ
    5

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  10. ͜Ε͚ͩͩͱ Key Paths ͷࢫຯ͕ͳ͍
    user[keyPath: \.id] // 42
    user[keyPath: \.id] = 57
    ! dot syntax Ͱ୅༻Ͱ͖Δ
    user.id // 42
    user.id = 57

    View Slide

  11. Key Paths ͷخ͠͞ΛҰ෦ൈਮͯ͠঺հ
    • ӳจͷΑ͏ͳߏจʹͰ͖Δ


    • Ҿ਺ͱͯ͠ getter ͱ setter Λඞཁͱ͢ΔγʔϯͰҾ਺Λ

    ίϯύΫτʹͰ͖Δ


    • map ͳͲͷߴ֊ؔ਺Ͱ΋ར༻Ͱ͖Δ
    11

    View Slide

  12. Key Paths ͷخ͠͞ΛҰ෦ൈਮͯ͠঺հ
    • ӳจͷΑ͏ͳߏจʹͰ͖Δ


    • Ҿ਺ͱͯ͠ getter ͱ setter Λඞཁͱ͢ΔγʔϯͰҾ਺Λ

    ίϯύΫτʹͰ͖Δ


    • map ͳͲͷߴ֊ؔ਺Ͱ΋ར༻Ͱ͖Δ
    12

    View Slide

  13. ӳจͷΑ͏ͳߏจʹ / Ҿ਺ΛίϯύΫτʹ Ͱ͖Δ
    class Label {
    var text = ""
    var fontSize = 12
    }
    class ProductModel {
    var name = "kalupas"
    }
    let model = ProductModel()
    let label = Label()

    View Slide

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

    View Slide

  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)

    View Slide

  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)

    View Slide

  17. Key Paths ͷخ͠͞ΛҰ෦ൈਮͯ͠঺հ
    • ӳจͷΑ͏ͳߏจʹͰ͖Δ


    • Ҿ਺ͱͯ͠ getter ͱ setter Λඞཁͱ͢ΔγʔϯͰҾ਺Λ

    ίϯύΫτʹͰ͖Δ


    • map ͳͲͷߴ֊ؔ਺Ͱ΋ར༻Ͱ͖Δ
    17

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  21. map ͳͲͷߴ֊ؔ਺Ͱ΋ར༻Ͱ͖Δ
    struct User {
    var name: String
    }
    let users = [
    .init(...),
    .init(...),
    // ...
    ]
    let userNames = users.map(\.name)
    0249-key-path-literal-function-
    expressions ʹΑ࣮ͬͯݱ͞Εͨ

    View Slide

  22. ΋ͪΖΜଞʹ΋ Key Paths ͕༗༻ͳ৔໘͸͋Δ
    • ෳࡶͳϓϩύςΟ΁ͷΞΫηε


    • KeyPath Member Lookup Λར༻ͨ͠ܕ҆શͳϓϩύςΟ΁ͷΞΫηε


    • SwiftUI


    • ʮ@StateʯΛʮ$user.nameʯͷΑ͏ʹѻ͑Δ


    • ʮ@Environment(\.dismiss)ʯ


    • ForEach ͳͲͰΑ͘ݟ͔͚Δʮ\.selfʯ(Identity key path) ͳͲ΋


    • ଞʹ΋୔ࢁ͋Δͱࢥ͍·͕͢ɺ঺հ͖͠Εͳ͍ͷͰׂѪ͠·͢
    22

    View Slide

  23. Case Paths ʹ͍ͭͯ

    View Slide

  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

    View Slide

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

    View Slide

  26. Case Paths ͷجຊ
    enum Authentication {
    case authenticated(accessToken: String)
    case unauthenticated
    }
    /Authentication.authenticated // CasePath
    ʮ/Enum.Caseʯͱ͍͏ߏจͰ

    CasePath ΛखʹೖΕΒΕΔ

    View Slide

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

    View Slide

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

    View Slide

  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
    }

    View Slide

  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
    }

    View Slide

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

    View Slide

  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")

    View Slide

  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
    }

    View Slide

  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)

    View Slide

  35. Case Paths ͷଞͷػೳ - Composition -
    \HighScore.user.name
    // WritableKeyPath
    /Result..Authentication.authenticated
    // CasePath, String>

    View Slide

  36. Case Paths ͷଞͷػೳ - Identity case path -
    \User.self // WritableKeyPath
    /Authentication.self // CasePath

    View Slide

  37. Case Paths ͷར༻ํ๏
    • Key Paths ͱಉ͘͡ɺgetterɾsetter ͚ͩͷ໾ׂͰ Case Paths Λ

    ར༻ͯ͠΋ࢫຯΛಘΒΕΔέʔε͸গͳ͍


    • Caes Paths ͷར༻ྫΛݟ͍ͯ͘͜ͱͰศར͞Λগ͚ͩ͠঺հ͢Δ


    • map ͳͲͷߴ֊ؔ਺Ͱͷར༻


    • swiftui-navigation
    37

    View Slide

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

    View Slide

  39. map ͳͲͷߴ֊ؔ਺Ͱͷར༻
    // Key Paths
    users.map(\User.name)
    // Case Paths
    authentications.compactMap(/Authentication.authenticated)
    Case Paths ͷॲཧ͸ɺcase ʹ associated value ͕

    ଘࡏ͠ͳ͍৔߹ nil ʹͳΔͨΊɺcompactMap ͳͲΛར༻͢Δ

    View Slide

  40. swiftui-navigation
    • Point-Free ੡ͷ SwiftUI Navigation ʹ focus ͨ͠ϥΠϒϥϦ


    • https://github.com/pointfreeco/swiftui-navigation


    • iOSDC Japan 2022 Ͱൃදͨ͠ͷͰৄ͘͠͸ͦͪΒΛࢀর͍ͩ͘͞🙏


    • ʮSwiftUI Navigation ͷ͢΂ͯʯ


    • swiftui-navigation ͷ಺෦Ͱ΋ Case Paths ͕ར༻͞Ε͍ͯΔ
    40

    View Slide

  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)
    }
    }
    }

    View Slide

  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)
    }
    }
    }

    View Slide

  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)
    }
    }
    }

    View Slide

  44. swiftui-navigation
    extension View {
    func sheet(
    unwrapping enum: Binding,
    case casePath: CasePath,
    @ViewBuilder content: @escaping (Binding) -> Content
    ) -> some View where Content: View {
    self.sheet(
    isPresented: `enum`.case(casePath).isPresented()
    ) {
    Binding(
    unwrapping: `enum`.case(casePath)
    ).map(content)
    }
    }
    }

    View Slide

  45. Case Paths ͷ͓͔͛Ͱ enum ͷಛఆͷ case ʹ

    Ԡͨ͡ॲཧΛهड़Ͱ͖ΔΑ͏ʹͳΔ
    extension View {
    func sheet(
    unwrapping enum: Binding,
    case casePath: CasePath,
    @ViewBuilder content: @escaping (Binding) -> Content
    ) -> some View where Content: View {
    self.sheet(
    isPresented: `enum`.case(casePath).isPresented()
    ) {
    Binding(
    unwrapping: `enum`.case(casePath)
    ).map(content)
    }
    }
    }
    Enum ͷಛఆͷ case ͕
    active ͳ࣌ʹͷΈ

    දࣔ͞ΕΔ sheet

    View Slide

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


    • @_disfavoredOverload


    • Mirror


    • Pointer
    46

    View Slide

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


    • @_disfavoredOverload


    • Mirror


    • Pointer


    • ࠓ೔ͷൃද·Ͱʹઆ໌Ͱ͖ΔϨϕϧ·Ͱམͱ͠ࠐΊͳ͔ͬͨͷͰɺ

    ·ͨͷػձʹهࣄʹ͢Δ͔ൃද͠Α͏ͱࢥ͍·͢🙇
    47

    View Slide

  48. Case Paths ͷ࣮૷໘നϙΠϯτ
    • pre
    fi
    x, in
    fi
    x operator


    • @_disfavoredOverload


    • Mirror


    • Pointer
    48

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

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

    View Slide

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

    ϧʔϧΛ࣋ͨͤΔ͜ͱ͕Ͱ͖Δ
    associativity: leftͰ

    operator ʹࠨ݁߹ͷ݁߹ੑΛ࣋ͨͤΒΕΔ
    (1 + 2) + 3 Ͱ͸ͳ͘ 1 + 2 + 3

    Ͱߦ͚ΔΑ͏ʹ͢ΔΠϝʔδ

    View Slide

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

    View Slide

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

    View Slide

  61. Case Paths ͷ࣮૷໘നϙΠϯτ
    • pre
    fi
    x, in
    fi
    x operator


    • @_disfavoredOverload


    • Mirror


    • Pointer
    61

    View Slide

  62. Swift Ͱ͸ function overload ͕ڐ͞Ε͍ͯΔ
    func sameName() {}
    func sameName(value: Int) {}
    func sameName() {}
    func sameName(value: Int = 5) {}
    func sameName(_ number: Int) {}
    func sameName(_ number: N) {}
    func sameName(_ number: Int) {}
    func sameName(_ numbers: Int...) {}

    View Slide

  63. overload ͞Ε͍ͯΔ৔߹ɺΑΓ۩ମతͳ΋ͷ͕

    ༏ઌ͞Εͯݺ͹ΕΔ (ެࣜʹ͸ݴٴ͞Ε͍ͯͳ͍)
    func sameName() {}
    func sameName(value: Int = 5) {}
    func sameName(_ number: Int) {}
    func sameName(_ 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/

    View Slide

  64. overload ͞Ε͍ͯΔ৔߹ɺΑΓ۩ମతͳ΋ͷ͕

    ༏ઌ͞Εͯݺ͹ΕΔ (ެࣜʹ͸ݴٴ͞Ε͍ͯͳ͍)
    func sameName() {} // sameName() Ͱͪ͜Β͕ݺ͹ΕΔ
    func sameName(value: Int = 5) {}
    func sameName(_ number: Int) {} // sameName(5) Ͱͪ͜Β͕ݺ͹ΕΔ
    func sameName(_ 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/

    View Slide

  65. sameName(value:) Λ༏ઌͯ͠ݺ͹ͤΔํ๏
    func sameName() {} // sameName() Ͱͪ͜Β͕ݺ͹ΕΔ
    func sameName(value: Int = 5) {}

    View Slide

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

    View Slide

  67. sameName(value:) Λ༏ઌͯ͠ݺ͹ͤΔํ๏
    @_disfavoredOverload
    func sameName() {}
    func sameName(value: Int = 5) {} // sameName() Ͱͪ͜Β͕ݺ͹ΕΔΑ͏ʹͳΔ
    `@_disfavoredOverload` ʹʮ_ (ΞϯμʔείΞ)ʯ͕

    ෇͍͍ͯΔͷ͸ɺSwift Evolution Λ௨ա͍ͯ͠ͳ͍ͨΊɻ
    Library headers ʹ͸ `@_disfavoredOverload` Ͱ͋Δ͜ͱ͕

    දࣔ͞Εͳ͍ͨΊɺdocument comment Λ͚ͭΔ͜ͱ͕ॏཁ

    View Slide

  68. Case Paths Ͱ΋ @_disfavoredOverload ͕

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

    View Slide

  69. Case Paths Ͱ΋ @_disfavoredOverload ͕

    ར༻͞Ε͍ͯΔ
    public prefix func / (
    embed: @escaping (Value) -> Root
    ) -> CasePath {
    .init(embed: embed, extract: extractHelp(embed))
    }
    @_disfavoredOverload
    public prefix func / (
    embed: @escaping (Value) -> Root
    ) -> (Root) -> Value? {
    (/embed).extract(from:)
    }
    ීஈ `/Authentication.authenticated` ͷΑ͏ʹ

    ར༻ͨ͠৔߹ɺ্ͷ function ͕༏ઌ͞Εͯݺ͹ΕΔ


    `compactMap` ͳͲͰར༻ͨ࣌͠
    = `(Root) -> Value?` Λඞཁͱ͢Δ࣌
    ͷΈͰԼͷ function ͕ݺ͹ΕΔΑ͏ʹͳ͍ͬͯΔ

    View Slide

  70. Case Paths ͷ࣮૷໘നϙΠϯτ
    • pre
    fi
    x, in
    fi
    x operator


    • @_disfavoredOverload


    • Mirror


    • Pointer
    70

    View Slide

  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

    View Slide

  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))

    View Slide

  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))

    View Slide

  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

    View Slide

  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"]

    View Slide

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

    View Slide

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

    View Slide

  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"

    View Slide

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

    View Slide

  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 ʹ͸ଘࡏ͍ͯ͠ͳ͍

    View Slide

  81. Mirror ʹΑΔ extract ͷ࣮૷ํ๏

    (આ໌ͷͨΊͷෆ׬શͳ࣮૷ʣ
    func extract(
    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
    }

    View Slide

  82. Mirror ʹΑΔ extract ͷ࣮૷ํ๏

    (આ໌ͷͨΊͷෆ׬શͳ࣮૷ʣ
    func extract(
    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”)

    View Slide

  83. Mirror ʹΑΔ extract ͷ࣮૷ํ๏

    (આ໌ͷͨΊͷෆ׬શͳ࣮૷ʣ
    func extract(
    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

    View Slide

  84. Mirror ʹΑΔ extract ͷ࣮૷ํ๏

    (આ໌ͷͨΊͷෆ׬શͳ࣮૷ʣ
    func extract(
    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")

    View Slide

  85. Mirror ʹΑΔ extract ͷ࣮૷ํ๏

    (આ໌ͷͨΊͷෆ׬શͳ࣮૷ʣ
    func extract(
    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 Λฦ͢

    View Slide

  86. ೚ҙͷ enum ͷ associated value Λ

    நग़Ͱ͖ΔΑ͏ʹͳͬͨ
    extract(case: Authentication.authenticated, from: auth) // AccessToken
    extract(case: Authentication.authenticated, from: .unauthenticated) // nil
    extract(case: Result.success, from: .success(42)) // 42
    struct MyError: Error {}
    extract(case: Result.failure, from: .failure(MyError()))
    // MyError
    extract(case: Example.foo, from: .foo(2)) // 2
    extract(case: Example.bar, from: .foo(2)) // nil

    View Slide

  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

    View Slide

  88. Case Paths ͷར༻ʹෆ͕҆͋Δํ΁

    View Slide

  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 ͸͔ͬ͠Γςετ͞Ε͍ͯͯར༻࣮੷΋͋Γɺݴޠػೳʹ૊ΈࠐΉͷͨΊͷಈ͖΋ൃੜ͍ͯ͠Δ

    View Slide

  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

    View Slide

  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

    View Slide

  92. ิ଍ࢿྉ

    View Slide

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

    View Slide

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

    View Slide

  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 Λදݱ͢ΔͨΊʹ
    Ҿ਺͕ଟ͘ͳΔ

    View Slide

  96. bind ͷྫͷ৔߹ɺҎԼͷΑ͏ʹ͸Ͱ͖Δ
    bind(
    model: model,
    get: { $0.name },
    to: label,
    get: { $0.text },
    set: { $0.text = $1 }
    )
    !
    bind(model.name, to: label)

    View Slide

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

    View Slide

  98. Combine Ͱ΋ࣅͨΑ͏ͳߏจ͕͋Δ
    let subject = PassthroughSubject()
    subject.assign(to: \.text, on: label)
    • ӳจͷΑ͏ʹಡΊΔ
    • Ҿ਺͕ίϯύΫτʹͳΔ

    View Slide