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

SwiftUI時代のFunctional iOS Architecture / iOSDC Japan 2020

Yasuhiro Inami
September 21, 2020

SwiftUI時代のFunctional iOS Architecture / iOSDC Japan 2020

SwiftUI時代の Functional iOS Architecture by 稲見 泰宏 | トーク | iOSDC Japan 2020 - fortee.jp (Sep 21, 2020)
https://fortee.jp/iosdc-japan-2020/proposal/6d88c4dc-5a24-4c07-b505-65e2c389cdfb

Blog: https://zenn.dev/inamiy/articles/26b41ae9d81b83d5e9be
Book: https://zenn.dev/inamiy/books/795a2d9e6954abb46878

Yasuhiro Inami

September 21, 2020
Tweet

More Decks by Yasuhiro Inami

Other Decks in Programming

Transcript

  1. SwiftUI ࣌୅ͷ
    Functional
    iOS Architecture
    2020/09/21 iOSDC Japan 2020
    Yasuhiro Inami / @inamiy

    View Slide

  2. View Slide

  3. View Slide

  4. View Slide

  5. ɹhttps://speakerdeck.com/inamiy/reactive-state-machine-japanese

    View Slide

  6. Reactive State Machine ͷৼΓฦΓ
    • MVVM ͔Β Redux / Elm Architecture (Mealy Machine) ΁
    • Reducer = (Action, State) -> (State, Output)
    • Output = Publisher
    • ΞϓϦͷσʔλϑϩʔΛ୯ํ޲ͷϧʔϓʹ୯७Խ͠ɺঢ়ଶ؅
    ཧͱςετΛ͠΍͘͢͢Δ
    • ෭࡞༻ (IO) ʹؔ਺ܕϦΞΫςΟϒϓϩάϥϛϯά (FRP) Λ༻
    ͍Δ

    View Slide

  7. Reactive State Machine ͷৼΓฦΓ
    • Proof of Concept
    • ReactiveAutomaton (ReactiveSwift൛)
    • RxAutomaton (RxSwift൛)
    • ͦͷଞ
    • React & Elm inspired frameworks in Swift
    • SwiftElmɿUIKit্ͷԾ૝ViewϑϨʔϜϫʔΫʢࢼݧ࣮૷ʣ

    View Slide

  8. ͋Ε͔Β4೥

    View Slide

  9. View Slide

  10. SwiftUI
    Combine

    View Slide

  11. View Slide

  12. Reactive State Machine ͷޙܧ࡞ʮHarvestʯΛΦʔϓϯιʔε

    View Slide

  13. ࠓ೔ͷΞδΣϯμ
    SwiftUI ࣌୅ͷ Functional iOS Architecture
    • inamiy/Harvest
    • pointfreeco/swift-composable-architecture
    • bow-swift/bow-arch
    • 3ͭͷϑϨʔϜϫʔΫͷڞ௨఺ͱҧ͍ʹ͍ͭͯ
    • ReactɺElm ͳͲͷWebϑϩϯτΤϯυͱͷൺֱ

    View Slide

  14. !
    Harvest

    View Slide

  15. !
    Harvest
    • Reactive State Machine (Elm Architecture෩) ࣮૷Λվྑ
    • SwiftUI + Combine ରԠ (HarvestStore)
    • Dependency Injection ίϯςφͱͯ͠΋ಇ͘
    • FRPʹΑΔ ෭࡞༻ͷΩϡʔ؅ཧ ͱΩϟϯηϧॲཧ
    • Optics (Lens & Prism) Λ࢖ͬͨ State ͱ Action ͷ෦඼Խͱ
    Reducer ͷ߹੒

    View Slide

  16. ෭࡞༻ΛΩϡʔͰ؅ཧ͢Δ
    • Publisher (Observable): ࣌ؒґଘͷ෭࡞༻ͷετϦʔϜ
    • ετϦʔϜͷετϦʔϜ ( Publisher> ) ͸ɺ
    ෭࡞༻ͷετϦʔϜΛʮΩϡʔ؅ཧʯ͍ͯ͠Δ͜ͱʹ૬౰
    • Ωϡʔ؅ཧ͞Ε༷ͨʑͳετϦʔϜΛҰຊͷετϦʔϜʹ౷
    ߹͢ΔʢʹϞφυ߹੒ɺϑϥοτԽʣ
    • Ωϡʔ؅ཧͷ ౷߹ઓུ (FlattenStrategy)
    • merge, concat, concurrent(max:), switchLatest, race ͳͲ

    View Slide

  17. View Slide

  18. View Slide

  19. Elm Architecture + FRP Λ૊Έ߹ΘͤΔར఺
    • FRPͷຊ࣭͸ɺ Publisher ಺෦ͷঢ়ଶΛӅṭ͠ͳ͕Βσʔλ
    ϑϩʔΛߏஙͰ͖Δ ͜ͱ
    • ͨͩ͠ɺঢ়ଶΛӅṭ͢Ε͹͢Δ΄Ͳɺঢ়ଶ؅ཧ͕೉͘͠ͳΔ
    • Elm Architecture ʢσʔλϑϩʔͷ؆қԽͱঢ়ଶ؅ཧͷ௥
    ٻʣΛϕʔεʹɺFRP Ͱʮͭ·Βͳ͍ঢ়ଶ؅ཧʯΛল͘
    • ྫɿ throttle ࣌ͷલճೖྗ࣌ࠁͷه࿥ɺલड़ͷΩϡʔ؅
    ཧ͞Εͨ෭࡞༻ͷ౷߹ܭࢉͳͲ

    View Slide

  20. Optics
    (Lens & Prism)

    View Slide

  21. Optics (Lens & Prism)
    • Lens: ঢ়ଶʢ௨ৗ͸ struct ௚ੵܕʣʹର͢Δ2ͭͷૢ࡞
    • ྫɿϢʔβʔ໊ͷऔಘ (get) ͱߋ৽ (set)
    • Prism: ΞΫγϣϯ (௨ৗ͸ enum ௚࿨ܕ) ʹର͢Δ2ͭͷૢ࡞
    • ྫɿ֤ case ͔ΒίϚϯυੜ੒ & Φϓγϣϯऔಘ (tryGet)
    struct User { enum Command {
    var name: String case rm(rf: Bool)
    } }

    View Slide

  22. struct Lens {
    /// struct ϝϯόม਺ͷऔಘ
    let get: (Whole) -> Part
    /// struct ϝϯόม਺ͷߋ৽
    let set: (Whole, Part) -> Whole
    }
    struct Prism {
    /// enum case ͷ associated values ͷऔಘ
    let tryGet: (Whole) -> Part?
    /// case ؔ਺ʢenum ίϯετϥΫλʣ
    let build: (Part) -> Whole
    }

    View Slide

  23. View Slide

  24. View Slide

  25. View Slide

  26. View Slide

  27. View Slide

  28. View Slide

  29. View Slide

  30. View Slide

  31. View Slide

  32. View Slide

  33. View Slide

  34. View Slide

  35. View Slide

  36. Lens ͱ Prism Λ࢖ͬͯ
    Reducerͷ
    ܕม׵ ʴ ߹੒͕Մೳ

    View Slide

  37. Reducer ͕߹੒ՄೳͳΒ͹
    ঢ়ଶͱΞΫγϣϯΛ
    ֤ίϯϙʔωϯτ͝ͱʹ
    ෼ղͯ͠ઃܭͰ͖Δ

    View Slide

  38. /// Reducer ͷ ChildState Λ ਌ State ʹม׵ʢ൓มؔखʣ
    func contramapS
    (_ lens: Lens) // ਌͔Βࢠ΁ͷࣹ
    -> Reducer // ࢠ͔Β
    -> Reducer // ਌΁ͷࣹ
    /// Reducer ͷ ChildAction Λ ਌ Action ʹม׵ʢ൓มؔखʣ
    func contramapA
    (_ prism: Prism) // ਌͔Βࢠ΁ͷࣹ
    -> Reducer // ࢠ͔Β
    -> Reducer // ਌΁ͷࣹ

    View Slide

  39. Reducer ͷܕม׵ͱ߹੒
    func combine(reducers: [Reducer]) { ... }
    let childReducer1: Reducer = ...
    let childReducer2: Reducer = ...
    // ࢠίϯϙʔωϯτ1,2ͷ Reducer Λ਌Λج४ʹม׵ͯ͠߹੒͢Δ
    let reducer: Reducer
    = combine([
    contramapS(lens1)(contramapA(prism1)(childReducer1))
    contramapS(lens2)(contramapA(prism2)(childReducer2))
    ])

    View Slide

  40. Optics ·ͱΊ
    • ΞϓϦͷ State (ओʹ struct) ͱ Action (ओʹ enum) Λڊେʹ
    ͤͣɺπϦʔߏ଄ʹ෼ղͯ͠ɺݸʑͷίϯϙʔωϯτ͝ͱʹ
    ؅ཧ͢Δʢૄ݁߹Խʣ
    • ݸʑͷίϯϙʔωϯτͷ Reducer Λશମʹ·ͱΊΔ ࡍʹ
    Lens ͱ Prism ͕׆༂͢Δ (react-reduxͷ্Ґ൛)
    • Optics ͸ ؔ਺ܕϓϩάϥϛϯάͱݍ࿦ Λ࢖ͬͨ୅දతͳς
    ΫχοΫͷ1ͭ

    View Slide

  41. View Slide

  42. View Slide

  43. View Slide

  44. Harvest-SwiftUI-
    Gallery
    https://github.com/inamiy/Harvest-SwiftUI-Gallery

    View Slide

  45. Composable
    Architecture

    View Slide

  46. Composable Architecture (TCA)
    • Point-Free νʔϜʹΑΔ Elm Architecture ෩ͷ࣮૷
    • Multi-Storeํࣜɿ ࠷্ҐͰ͸ͳ͘ɺ਌ʢ্ྲྀʣͱࢠʢԼ
    ྲྀʣίϯϙʔωϯτ֤ʑʹରͯ͠ϦΞΫςΟϒʹΠϕϯτ
    Λ఻ൖ͢Δ ʴ ॏෳ๷ࢭॲཧ
    • swift-case-paths Λ࢖ͬͨ Prism ࣮૷
    • Lens ͸Swiftඪ४ͷ WritableKeyPath Λ࢖ͬͯ୅༻
    • ˑ2000Ҏ্ɺυΩϡϝϯτͱϏσΦνϡʔτϦΞϧ͕ॆ࣮

    View Slide

  47. View Slide

  48. View Slide

  49. View Slide

  50. View Slide

  51. View Slide

  52. Case Paths
    struct User {
    var name: String
    }
    // WritableKeyPath
    let keyPath = \User.name // όοΫεϥογϡ
    enum Command {
    case rm(rf: Bool)
    }
    // CasePath
    let casePath = /Command.rm // ௨ৗͷεϥογϡ

    View Slide

  53. struct CasePath { // `Prism` ͱಉܕ
    let embed: (Value) -> Root
    let extract: (Root) -> Value?
    }
    prefix func / (
    embed: @escaping (Value) -> Root
    ) -> CasePath { /*
    !
    Magic inside */ }
    " prefix func / " ͷ಺෦࣮૷
    Swift ͷϦϑϨΫγϣϯΛ࢖ͬͯɺembedʢcaseؔ਺ʣ͔Β
    extract Λࣗಈతʹಋग़͢Δ (ར఺ɿcodegen͕ཁΒͳ͍)

    View Slide

  54. ৄࡉ͸ͪ͜ΒͷηογϣϯΛ͝ཡ͍ͩ͘͞

    View Slide

  55. Bow Arch

    View Slide

  56. Bow Arch
    • Tomás Ruiz-López ࢯ (47 Degrees ࣾ) ࡞ͷ UI Architecture
    • Bow: Lightweight Higher Kinded Polymorphism (ߴΧΠϯυ
    ଟ૬) Λ࢖ͬͨؔ਺ܕϓϩάϥϛϯά༻ϥΠϒϥϦ
    • func foo ͷΑ͏ͳॻ͖ํ͕Ͱ͖Δ
    • ࢀߟɿSwiftͰߴΧΠϯυଟ૬ - Speaker Deck
    • Comonadic UI: ݍ࿦ʢ਺ֶʣΛ࢖ͬͨUIઃܭख๏

    View Slide

  57. Comonadic UI

    View Slide

  58. ίϞφυ ≈ ΦϒδΣΫτࢦ޲ − Մมࢀর
    • SwiftUI.View Λ ίϞφυ ͱͯ͠ߟ͑Δ
    • ίϞφυ: (಺෦)ঢ়ଶΛ࣋ͪɺঢ়ଶ͔Βܭࢉͨ݁͠ՌΛฦ͢
    • ྫɿΠςϨʔλ͸ɺ಺෦ঢ়ଶΛߋ৽͠ͳ͕Β next Λग़ྗ
    • ྫɿϏϧμʔ͸ɺड͚औͬͨύϥϝʔλΛ಺෦ঢ়ଶʹอ࣋
    ͯ͠ɺ࠷ऴ݁ՌΛग़ྗ
    • ྫɿReact Component (SwiftUI) ͸ɺঢ়ଶΛ࣋ͪͭͭɺ
    render (body) ͰԾ૝DOM (View) Λग़ྗ

    View Slide

  59. struct Component: View {
    // ݱࡏͷঢ়ଶ
    var state: S
    // ݱࡏͷঢ়ଶ͔ΒԾ૝ViewΛܭࢉ
    let _body: (S) -> V
    // Note: static var Ͱߟ͑Δͱɺܕ͸ `Self -> V`
    var body: V { _body(state) }
    }
    Component = W ͱ͓͘ͱɺ
    body: W -> V ɾɾɾ ίϞφυʹॏཁͳ extract ͷੑ࣭

    View Slide

  60. View Slide

  61. View Slide

  62. View Slide

  63. View Slide

  64. // Ϟφυ M = UFʢίϯςΩετΛੜ੒͢Δܭࢉʣɹɹɹ// NOTEɿٙࣅSwift
    protocol Monad[M] where Functor[M] {
    static func `return`(_ c: C) -> M // η = unit
    static func join(_ mmc: M>) -> M
    // static func flatMap(_ f: C -> M)
    // -> M -> M
    }
    // ίϞφυ W = FUʢίϯςΩετΛফඅ͢Δܭࢉʣ
    protocol Comonad[W] where Functor[W] {
    static func extract(_ wd: W) -> D // ε = counit
    static func duplicate(_ wd: W) -> W>
    // static func extend(_ f: W -> D2)
    // -> W -> W
    }

    View Slide

  65. ίϞφυ (Comonad)
    // ٙࣅSwift
    protocol Comonad[W] where Functor[W] {
    static func extract(_ wd: W) -> D
    static func duplicate(_ wd: W) -> W>
    }
    • extract: ΦϒδΣΫτ W ͔ΒίϯςΩετΛফඅͯ͠ D
    Λग़ྗʢe.g. ঢ়ଶΛ࢖ͬͯ View Λग़ྗʣ
    • duplicate: ΦϒδΣΫτͷऔΓ͏ΔະདྷਤΛੜ੒ʢޙड़ʣ

    View Slide

  66. duplicate ͷΠϝʔδʢྫɿແݶετϦʔϜʣ
    let stream = [ 0, 1, 2, ... ] // ݱ࣌఺ͷແݶετϦʔϜ
    // ʢݱࡏͱະདྷͷʣແݶετϦʔϜΛཁૉʹ࣋ͭແݶετϦʔϜ
    duplicate(stream)
    = [ [ 0, 1, 2, ... ], // ݱࡏͷ `stream` Λෳ੡
    [ 1, 2, 3, ... ], // ෳ੡ + shift
    [ 2, 3, 4, ... ], // ෳ੡ + shift 2ճ
    [ 3, 4, 5, ... ], // ෳ੡ + shift 3ճ
    ... ]

    View Slide

  67. duplicate ͷΠϝʔδʢྫɿSwiftUI / Reactʣ
    let makeComp: (S) -> Component = Component(_body: ...) // ෦෼ద༻
    let component: Component = makeComp(state: ...) // ݱࡏͷ Component
    // ʢݱࡏͱະདྷͷʣComponent Λੜ੒͢Δ Component
    duplicate(component)
    = Component(_body: makeComp, state: component.state)
    ≈ [ /* ݱࡏͷ Component ͷෳ੡ */,
    /* ෳ੡ + ঢ়ଶͷҰ෦͕มߋ͞Εͨ Component */,
    /* ෳ੡ + ঢ়ଶͷผͷҰ෦͕มߋ͞Εͨ Component */,
    ... ] // Component ͕औΓ͏Δશͯͷঢ়ଶͷۭؒ

    View Slide

  68. Component ≅ Store ίϞφυ
    struct Component {
    // ݱࡏͷঢ়ଶ
    var state: S
    // ݱࡏͷঢ়ଶ͔ΒԾ૝ViewΛܭࢉ
    let _body: (S) -> V
    }
    struct Store {
    let state: S
    let render: (S) -> A
    }

    View Slide

  69. ࣮ࡍͷ ComponentʢΠϕϯτϋϯυϥ෇͖ʣ
    struct Component {
    // ݱࡏͷঢ়ଶ
    var state: S
    // ݱࡏͷঢ়ଶͱʮΠϕϯτϋϯυϥʯΛ౉ͯ͠Ծ૝ViewΛܭࢉ
    let view: (S) -> (EventHandler) -> V
    }
    typealias EventHandler = (Action) -> IO /* ෭࡞༻ */

    View Slide

  70. ͜͜Ͱɺ
    typealias UI = (EventHandler) -> V
    ͱ͓͘ͱɺ
    struct Component {
    var state: S
    let view: (S) -> UI
    }
    Component ͸ Store> ͱ͍͏ܗͰද͞ΕΔ

    View Slide

  71. ίϞφυͷ಺෦ঢ়ଶΛ֎͔Βૢ࡞͢Δ
    ઌ΄ͲͷແݶετϦʔϜͷྫͰ͸ɺ shift ʢ0, 1, 2, l߭Ďůሑ
    Ⅲ⴬۷ƑҠቔƎཌྷ֒čǸǠǟƵ൐ƆƂDžƾȀDSLͰදͤΔʣ
    // ແݶετϦʔϜʢίϞφυʣ // Shiftૢ࡞ʢϞφυʣ
    indirect enum Stream { indirect enum Shift {
    case cons(A, Stream) case done(A)
    } case shift(Shift)
    }
    Q. ͜ΕΒ2ͭͷܕ͸ͲͷΑ͏ͳؔ܎ʹ͋Δͷ͔ʁ

    View Slide

  72. A. Stream ίϞφυͱ Shift Ϟφυ͸ʮϖΞ (Pairing)ʯΛͳ͢
    protocol Pairing[F, G] {
    // ݍ࿦Ͱݴ͏ɺDayʢΞϓϦΧςΟϒʣ߹੒ੵ͔Β߃౳ؔख΁ͷࣗવม׵
    static func pair(f: (A, B) -> C) -> F -> G -> C
    }
    extension Pairing[Shift, Stream] {
    static func pair(f: (A, B) -> C) -> Shift -> Stream -> C {
    switch (shift, stream) {
    case let (.done(a), .cons(b, _)):
    return f(a, b)
    case let (.shift(nextShift), .cons(_, nextStream)):
    return pair(f)(nextShift)(nextStream)
    }
    }
    }

    View Slide

  73. Pairing Λ࢖͏ͱɺϞφυͰද͞ΕΔঢ়ଶૢ࡞ʹΑͬͯɺίϞ
    φυͷະདྷΛܾΊΔ͜ͱ͕Ͱ͖Δɻ
    // `stream` ͷະདྷΛ `shift` ͰܾΊΔ
    func select(shift: Shift<()>, stream: Stream) -> Stream {
    pair({ _, stream in stream })(shift)(duplicate(stream))
    }
    // Ұൠతʹɺ೚ҙͷϞφυɾίϞφυͷϖΞʹ͍ͭͯ੒Γཱͭ
    func select(monad: M<()>, comonad: W) -> W
    where Monad[M], Comonad[W], Pairing[M, W]
    {
    pair({ _, comonad in comonad })(monad)(duplicate(comonad))
    }

    View Slide

  74. ͳ͓ɺ SwiftUI ্ͷ ComponentʢStore ίϞφυʣͷ৔߹ɺঢ়
    ଶૢ࡞ͱͯ͠ State ϞφυΛ࢖͏͜ͱ͕Ͱ͖Δʢཧ༝͸ޙड़ʣ
    struct Store {
    let state: S
    let render: (S) -> A
    }
    struct State {
    let runState: S -> (A, S)
    }
    extension Pairing[State, Store] { ... }

    View Slide

  75. Stream ίϞφυ → Shift Ϟφυ
    Store ίϞφυ → State Ϟφυ
    Q. ίϞφυͷϖΞͱͳΔϞφυ͸
    ͲͷΑ͏ʹݟ͚ͭΔͷ͔ʁ

    View Slide

  76. // ݍ࿦Ͱݴ͏ɺComonadʹԊͬͨ߃౳ؔखͷӈKan Liftɻ
    // ೚ҙͷίϞφυ `W` ʹରԠ͢Δʢঢ়ଶૢ࡞ͷʣϞφυ `Co` Λߏ੒͢Δ͜ͱ͕Մೳɻ
    struct Co {
    let runCo: W R> -> R
    }
    extension Monad[Co] where Comonad[W] {
    static func `return`(_ c: C) -> Co {
    Co { wf in W.extract(wf)(c) }
    }
    static func join(_ mmc: Co>) -> Co
    Co { (wc2r: W R>) in
    mmc.runCo(
    W.extend({ wc2r in { (mc: Co) in mc.runCo(wc2r) } })(wc2r)
    )
    }
    }
    }

    View Slide

  77. Co Λ࢖͏ͱɺྫ͑͹
    Co, A> ≅ State ΛಘΔɻ
    ূ໌ɿ
    Co ≅ ∀R. W R> -> R ΑΓ
    Co, A>
    ≅ ∀R. Store R> -> R
    ≅ ∀R. (S, (S -> A -> R)) -> R
    ≅ S -> ∀R. ((S, A) -> R) -> R // ΧϦʔԽ
    ≅ S -> (S, A) // ถాͷิ୊
    ≅ State

    View Slide

  78. ͜͜·Ͱͷ੔ཧ (Comonadic UI)
    • ίϞφυͷ extract ʹΑͬͯɺঢ়ଶ͔ΒViewΛੜ੒͢Δ
    • ίϞφυͷ duplicate ʹΑͬͯɺUIͷະདྷਤΛ࡞Δ
    • ίϞφυͷϖΞͱͳΔ ঢ়ଶૢ࡞༻ͷϞφυΛ Co Ͱࢦ
    ఆ ͢Δ
    • select ͰίϞφυͱঢ়ଶૢ࡞༻ϞφυͷϖΞ͔Βɺ ະདྷͷ
    UI༻ίϞφυΛબ୒͢Δ

    View Slide

  79. ίϞφυ × ෭࡞༻ (IO)
    // ίϞφυΛϥοϓͯ͠ɺະདྷબ୒ͱ෭࡞༻ͷ࣮ߦʢՄมࢀরͷߋ৽ΛؚΉʣΛߦ͏
    class EffectComponent: ObservableObject where Comonad[W] {
    @Published var comonad: W>
    func explore() -> V {
    W.extract(comonad) { (action: IO>) in
    action.flatMap { (monad: Co) in
    let nextComonad = select(monad, self.comonad.duplicate())
    return IO.invoke {
    self.comonad = nextComonad
    }
    }
    }
    }
    }

    View Slide

  80. ίϞφυ × ෭࡞༻ = ΦϒδΣΫτࢦ޲ϓϩάϥϛϯά (OOP)
    • EffectComponent ͸ OOPʹ͓͚ΔʮΦϒδΣΫτʯ
    ʢͦͷෳࡶ͞ΛʮίϞφυʯͱʮ෭࡞༻ʯʹ෼ղ͢Δʣ
    • W = Store ίϞφυͷ৔߹ɺ ϖΞͱͳΔ ঢ়ଶϞφυͱͦΕʹ
    ൐͏෭࡞༻͸ɺ setState ʢঢ়ଶͷʮ௚઀ߋ৽ʯʣͱ౳Ձ
    • React ΍ SwiftUIʢ@State ͷՄมࢀরʣͷੈք؍ͱҰக
    • Note: ίϯϙʔωϯτͷωετ͸ɺίϞφυม׵ࢠΛ࢖͏

    View Slide

  81. Q. EffectComponent ͸ɺ
    ଞͷίϞφυ W ʹ΋࢖͑ΔͷͰ͸ʁ !

    View Slide

  82. Moore ίϞφυ
    indirect enum Moore { // I = ೖྗɺ A = ग़ྗ
    // ݱࡏͷग़ྗͱɺʮೖྗ͔ΒະདྷͷMooreίϞφυʯͷੜ੒ؔ਺
    case runMoore(A, I -> Moore)
    }
    ∃S. (initial: S, reducer: S -> I -> S, render: S ->
    A) ͱಉܕ (Moore State Machineɺঢ়ଶͷʮؒ઀ߋ৽ʯ)
    ࣮૷ྫɿ Elm Architecture, Redux

    View Slide

  83. ༨ࣗ༝ίϞφυ (Cofree)
    // ೚ҙͷؔख `F` ʹରͯ͠ɺίϞφυΛ࡞Δ͜ͱ͕Ͱ͖Δɻ
    // `F(X) = I -> X` ͷͱ͖ɺMooreɻ`F(X) = ()` ͷͱ͖ɺ㱽S.Storeɻ
    indirect enum Cofree {
    case runCofree(A, F>)
    }
    ϖΞͱͳΔࣗ༝Ϟφυ (Free) ͕ΫΤϦDSLʹͳΔ
    ࣮૷ྫɿ PureScript Halogen

    View Slide

  84. Comonad → Architecture
    Store → React
    Moore → Elm
    Cofree → PureScript

    View Slide

  85. ίϞφυ͸
    ΞʔΩςΫνϟʔΛ
    نఆ͢Δ

    View Slide

  86. View Slide

  87. ·ͱΊ

    View Slide

  88. ·ͱΊ
    • SwiftUI ͷຊ࣭͸ ʮίϞφυʯ
    • ίϞφυͷߏ଄͕ UIΞʔΩςΫνϟʔͷύλʔϯ ΛܾΊΔ
    • React, Elm, PureScript Halogen, etc...
    • ঢ়ଶɺΞΫγϣϯɺ Reducer Λ֤ίϯϙʔωϯτ͝ͱʹ෼ղ
    ͠ɺ Optics Λ࢖ͬͯ·ͱΊ্͛Δ
    • ؔ਺ܕϓϩάϥϛϯάͱݍ࿦ɿ ຊ࣭Λཧղ͢Δڧྗͳख๏

    View Slide

  89. Harvest TCA Bow Arch
    GitHub Stars ˑ 300 ˑ 2000 ˑ 100
    ࡞ऀͷ΍Δؾ

    ೉қ౓ Medium Medium Hard
    ෭࡞༻ Combine Combine BowEffects
    Optics FunOptics WritableKeyPath
    & CasePaths
    BowOptics
    Comonadic UI Moore Moore Any Comonads
    ӨڹΛड͚ͨݴޠ Elm Elm & React PureScript & ݍ࿦

    View Slide

  90. References (Libraries & Optics)

    !
    Harvest: Apple's Combine.framework + State Machine,
    inspired by Elm
    • Composable Architecture
    • bow-arch: Comonadic UIs
    • Brandon Williams - Lenses in Swift
    • Lenses and Prisms in Swift: a pragmatic approach | Fun iOS

    View Slide

  91. References (Comonadic UI)
    • Declarative UIs are the Future — And the Future is
    Comonadic!
    • The Future Is Comonadic! - Speaker Deck
    • Comonads for user interfaces - Arthur Xavier
    • A Real-World Application with a Comonadic User Interface
    • The Comonad.Reader » Monads from Comonads

    View Slide

  92. References (෬ઢճऩ)
    • Reactive State Machine (Japanese) - Speaker Deck
    • SwiftͰElmΛ࡞Δ (Japanese) - Speaker Deck
    • SwiftͰߴΧΠϯυଟ૬ - Speaker Deck
    • ݍ࿦ͱSwift΁ͷԠ༻ / iOSDC Japan 2018 - Speaker Deck
    • ϓϩάϥϚͷͨΊͷϞφυ(ݍ࿦) - Speaker Deck
    • ݍ࿦ͱϓϩάϥϛϯά - Speaker Deck

    View Slide

  93. Thanks!
    Yasuhiro Inami
    @inamiy

    View Slide