Upgrade to PRO for Only $50/Year—Limited-Time Offer! 🔥

Swift アクターモデルと Elm Architecture の融合 / iOSDC Jap...

Yasuhiro Inami
September 11, 2022

Swift アクターモデルと Elm Architecture の融合 / iOSDC Japan 2022

Swift アクターモデルと Elm Architecture の融合 by 稲見 泰宏 | トーク | iOSDC Japan 2022 - fortee.jp
https://fortee.jp/iosdc-japan-2022/proposal/aa8ecff3-682b-4127-93ac-321b9ec4cf30

Yasuhiro Inami

September 11, 2022
Tweet

More Decks by Yasuhiro Inami

Other Decks in Programming

Transcript

  1. actor Counter { var count: Int = 0 // ಺෦ঢ়ଶ

    func increment() { // τϥϯβΫγϣϯ count += 1 // ঢ়ଶߋ৽ print(count) // ෭࡞༻ͷ࣮ߦ } } // ΞΫλʔͷ࢖༻ྫ let counter = Counter() // ΞΫλʔ֎෦͔ΒΞΫλʔʹΞΫηε͢Δ৔߹ɺ async / await ඇಉظॲཧʹͳΔ await counter.increment() ΞΫλʔͷྫ ΞΫλʔঢ়ଶͱͦͷૢ࡞ΛҰՕॴʹ·ͱΊͨ ʮεϨου҆શʯͳΦϒδΣΫτ ΞΫλʔִ཭͞Εͨϝιου εϨου҆શΛอূ͢Δ τϥϯβΫγϣϯ
  2. protocol CounterProtocol: Actor { func increment() // τϥϯβΫγϣϯͷந৅ } actor

    Counter { var count: Int = 0 // ಺෦ঢ়ଶ } extension Counter: CounterProtocol { func increment() { // τϥϯβΫγϣϯͷ࣮૷ count += 1 // ঢ়ଶߋ৽ print(count) // ෭࡞༻ͷ࣮ߦ } } ΞΫλʔͷྫ 1SPUPDPMʹΑΔந৅Խ  QSPUPDPMʹΑΔ 
 ۩ମΞΫλʔͷ ϝιουͷந৅Խ QSPUPDPM४ڌʹΑΔ 
 ந৅ϝιουͷ࣮૷
  3. Msg (Action) 􀉭 Real View Update (Reducer) State Virtual View

    view (render) Diff (New State) User Action Cmd (Effect) 💥 Runtime I/O Run Side-effect
  4. enum Action: Sendable { case increment } struct State: Sendable,

    Equatable { var count: Int = 0 } let reducer = Reducer { action, state, _ in switch action { // ΞΫγϣϯͷύλʔϯϚον case .increment: state.count += 1 // ঢ়ଶߋ৽ return Effect { [state] in print(state.count) // ෭࡞༻ͷ࣮ߦ } } } &MN"SDIJUFDUVSFͷྫ 4XJGU຋༁ ܭࢉϩδοΫͷೖྗ 
 ΞΫγϣϯ ΛFOVNͰදݱ ܭࢉϩδοΫ 3FEVDFS  ʮ"DUJPOʯͱʮݱࡏͷঢ়ଶʯΛ 
 Ҿ਺Ͱड͚औΓɺʮ৽͍͠ঢ়ଶʯͱ 
 ʮ෭࡞༻ʯΛฦؔ͢਺ ෭࡞༻ QSJOU΍BTZOD Λ & ff FDUͷதͰ࣮ߦ͢Δ
  5. enum Action: Sendable { case increment } struct State: Sendable,

    Equatable { var count: Int = 0 } let reducer = Reducer { action, state, _ in switch action { // ΞΫγϣϯͷύλʔϯϚον case .increment: state.count += 1 // ঢ়ଶߋ৽ return Effect { [state] in print(state.count) // ෭࡞༻ͷ࣮ߦ } } } &MN"SDIJUFDUVSFͷྫ 4XJGU຋༁ Elm Architecture Ͱ͸ class / actor ͕ొ৔͠ͳ͍ (ΑΓ୯७ͳ enum, struct, func ͕ओ໾)
  6. // Elm Architecture enum Action: Sendable { case increment }

    struct State: Sendable, Equatable { var count: Int = 0 } let reducer = Reducer { action, state, _ in switch action { // ΞΫγϣϯͷύλʔϯϚον case .increment: state.count += 1 // ঢ়ଶߋ৽ return Effect { [state] in print(state.count) // ෭࡞༻ͷ࣮ߦ } } } &MN"SDIJUFDUVSFͱΞΫλʔϞσϧͷൺֱ // ΞΫλʔϞσϧ protocol CounterProtocol: Actor { func increment() // τϥϯβΫγϣϯͷந৅ } actor Counter { var count: Int = 0 // ಺෦ঢ়ଶ } extension Counter: CounterProtocol { func increment() { // τϥϯβΫγϣϯͷ࣮૷ count += 1 // ঢ়ଶߋ৽ print(count) // ෭࡞༻ͷ࣮ߦ } }
  7. enum Action { case increment } struct State { var

    count: Int = 0 } let reducer = Reducer { action, state in switch action { case .increment: state.count += 1 return Effect { print(state.count) 
 } } } &MN"SDIJUFDUVSFͷྫ 4XJGU຋༁ protocol CounterProtocol: Actor { func increment() } actor Counter { var count: Int = 0 } extension Counter: CounterProtocol { func increment() { count += 1 print(count) } } Actor = ΦϒδΣΫτࢦ޲ Elm Architecture = ؔ਺ܕ ྆ऀʹີ઀ͳؔ܎͕͋Δʂ
  8. ࠷ۙͷ4XJGUJ04։ൃͷτϨϯυ  .77. 7JFX.PEFM ʹΞΫλʔϞσϧΛద༻ w !.BJO"DUPSʹΑΔϝΠϯεϨου҆શੑ  $PNQPTBCMF"SDIJUFDUVSF 5$"

     &MNελΠϧ  w ঢ়ଶͷू໿؅ཧʹΑΔςετɾσόοΪϯά 🤔 2྆ํͷྑ͍ॴऔΓ͕Ͱ͖ͳ͍΋ͷ͔ʜʁ
  9. enum Action: Sendable { case increment } struct State: Sendable,

    Equatable { var count: Int = 0 } let reducer = Reducer { action, state, _ in switch action { // ΞΫγϣϯͷύλʔϯϚον case .increment: state.count += 1 // ঢ়ଶߋ৽ return Effect.fireAndForget { [state] in print(state.count) // ෭࡞༻ͷ࣮ߦ } } } ঢ়ଶ؅ཧͷίʔυྫ ϩδοΫ 3FEVDFSͷجຊܗɿ ύλʔϯϚον ঢ়ଶͷόϦσʔγϣϯͱߋ৽ ෭࡞༻ͷొ࿥ 
 ʢඇಉظܭࢉޙͷ࣍ͷΞΫγϣϯ ΋ࢦఆՄೳʣ Ϋϩʔδϟʹแ·Εͨ෭࡞༻͸ʮ࣮ߦલʯ 
 ͳͷͰ3FEVDFS͸ʮ७ਮؔ਺ʯ
  10. enum Action: Sendable { // case ͕૿͑ͨ৔߹ case increment case

    decrementIfPositive } let reducer = Reducer { action, state, _ in switch action { // ΞΫγϣϯͷύλʔϯϚον case .increment: state.count += 1 // ঢ়ଶߋ৽ return Effect.fireAndForget { [state] in print(state.count) // ෭࡞༻ͷ࣮ߦ } case .decrementIfPositive: // ঢ়ଶͷόϦσʔγϣϯ guard state.count > 0 else { return Effect.empty } state.count -= 1 // ঢ়ଶߋ৽ return Effect.fireAndForget { [state] in print(state.count) // ෭࡞༻ͷ࣮ߦ } } }
  11. enum Action: Sendable { // case ͕૿͑ͨ৔߹ case increment case

    decrementIfPositive } let reducer = Reducer { action, state, _ in switch action { // ΞΫγϣϯͷύλʔϯϚον case .increment: state.count += 1 // ঢ়ଶߋ৽ return Effect.fireAndForget { [state] in print(state.count) // ෭࡞༻ͷ࣮ߦ } case .decrementIfPositive: // ঢ়ଶͷόϦσʔγϣϯ guard state.count > 0 else { return Effect.empty } state.count -= 1 // ঢ়ଶߋ৽ return Effect.fireAndForget { [state] in print(state.count) // ෭࡞༻ͷ࣮ߦ } } } ύλʔϯϚον ঢ়ଶͷόϦσʔγϣϯͱߋ৽ ෭࡞༻ͷొ࿥ ৗʹಉ͡εςοϓͰهड़͢ΔͨΊɺ ঢ়ଶͷมߋՕॴ͕෼͔Γ΍͍͢ "DUPS3FFOUSBODZ໰୊ͷ ݕ஌͕͠΍͍͢  ·ͨɺ3FEVDFS͸TFMG BDUPS Λ ؚ·ͳ͍ͷͰɺෆ஫ҙʹΑΔTFMG॥ ؀ࢀর͕ى͖ͳ͍҆શઃܭʹͳΔ
  12. let store = Store<Action, State, Void>( state: State(), // ॳظঢ়ଶ

    reducer: reducer ) struct ContentView: View { let store: Store<Action, State, Void> var body: some View { WithViewStore(store) { viewStore in Button("Count: \(viewStore.count)") { store.send(.increment) } } } } ঢ়ଶ؅ཧͷίʔυྫ 4UPSFԽͱ4XJGU6*ద༻ ΞΫγϣϯɺঢ়ଶɺ3FEVDFSΛ ଋͶͨ4UPSF .BJO"DUPS Λ࡞੒ 4UPSFΛ7JFX4UPSF 
 4XJGU6*!0CTFSWFE0CKFDU 
 ʹม׵ͯ͠࢖༻
  13. Reducer State Action CounterView Store 4UPSFΛ࡞Γɺ 7JFXʹ࣋ͨͤΔ (CounterLogic) 4UPSF ঢ়ଶ؅ཧΦϒδΣΫτ

     Λߏ੒͢ΔͨΊͷܕͱϩδοΫ ؔ਺ܕελΠϧ (߹੒͠΍͍͢) ΦϒδΣΫτࢦ޲ (߹੒͠ʹ͍͘) Reducer State Action
  14. ґଘ஫ೖͷίʔυྫ enum Action: Sendable { case fetch case _didFetch(Response) }

    struct State: Sendable, Equatable { var isLoading: Bool = false } struct Environment: Sendable { var fetch: @Sendable (Request) async throws -> Response } &OWJSPONFOU ґଘΛ஫ೖ͢Δܕ
  15. ґଘ஫ೖͷίʔυྫ let reducer = Reducer { action, state, environment in

    switch action { case .fetch: state.isLoading = true return Effect { let response = try await environment.fetch(Request(...)) return Action._didFetch(response) 
 } case let ._didFetch(response): state.isLoading = false return Effect.fireAndForget { print(response) } } } &OWJSPONFOUΛ࢖ͬͯ ෭࡞༻Λ࣮ߦ
  16. &GGFDU*%ʹΑΔ෭࡞༻ͷΩϟϯηϧ struct FetchEffectID: EffectIDProtocol {} let reducer = Reducer {

    action, state, environment in switch action { case .fetch: state.isLoading = true return Effect.cancel(id: FetchEffectID()) + Effect(id: FetchEffectID()) { let response = try await environment.fetch(Request(...)) return Action._didFetch(response) 
 } case let ._didFetch(response): state.isLoading = false return Effect.fireAndForget { print(response) } } } & ff FDU*%Λఆٛ & ff FDUʹ 
 & ff FDU*%Λઃఆ͠ɺ ΩϟϯηϧՄೳʹ͢Δ
  17. &GGFDU2VFVFʹΑΔ෭࡞༻؅ཧ struct FetchEffectQueue: Newest1EffectQueueProtocol {} let reducer = Reducer {

    action, state, environment in switch action { case .fetch: state.isLoading = true return Effect(queue: FetchEffectQueue()) { let response = try await environment.fetch(Request(...)) return Action._didFetch(response) 
 } case let ._didFetch(response): state.isLoading = false return Effect.fireAndForget { print(response) } } } & ff FDU2VFVFΛఆٛ /FXFTU͸࠷৽݅ͷΈ࣮ߦ & ff FDUʹ 
 & ff FDU2VFVFΛઃఆ͠ɺ ෳ਺ͷ෭࡞༻ΛΩϡʔͰ؅ཧ͢Δ
  18. &GGFDU2VFVFͷछྨ protocol EffectQueueProtocol: Hashable, Sendable { /// Runs newest or

    oldest with maxCount. var effectQueuePolicy: EffectQueuePolicy { get } /// Effect starting delay. var effectQueueDelay: EffectQueueDelay { get } } // Rx flatMapLatest / switchLatest protocol Newest1EffectQueueProtocol: EffectQueueProtocol { ... } // Rx flatMapFirst (or RxAction) protocol Oldest1DiscardNewEffectQueueProtocol: EffectQueueProtocol { ... } // Rx flatMapConcat protocol Oldest1SuspendNewEffectQueueProtocol: EffectQueueProtocol { ... }
  19. "DUPNBUPOΛ࢖ͬͨΞϓϦͷઃܭࢦ਑  ؔ਺ܕϓϩάϥϛϯάํࣜ 4JOHMF4PVSDFPG5SVUI  w"DUJPO 4UBUF 3FEVDFSͷ߹੒ͱ4UPSFͷ෼ղɾ࠷దԽ w௕ॴɿঢ়ଶͷू໿؅ཧɺ୹ॴɿ߹੒ͱ෼ղͷ೉қ౓͕ߴ͍ 

    ΦϒδΣΫτࢦ޲ํࣜ .VMUJ7JFX.PEFMT  w4UPSFΛ7JFX.PEFMͱಉҰࢹ͠ɺ߹੒ͱ෼ղΛߦΘͳ͍ w௕ॴɿ୭Ͱ΋ཧղͰ͖Δɺ୹ॴɿঢ়ଶͷ෼ࢄ؅ཧ ΞϓϦશମͰ 4UPSF͕ͭ ֤ը໘͝ͱʹ 4UPSFΛ༻ҙ
  20. Reducer State Action CounterView Store 4UPSFΛ࡞Γɺ 7JFXʹ࣋ͨͤΔ (CounterLogic) 4UPSF ঢ়ଶ؅ཧΦϒδΣΫτ

     Λߏ੒͢ΔͨΊͷܕͱϩδοΫ ؔ਺ܕελΠϧ (߹੒͠΍͍͢) ΦϒδΣΫτࢦ޲ (߹੒͠ʹ͍͘) Reducer State Action
  21. Reducer State Action (CounterLogic) Reducer State Action (DownloaderLogic) Reducer State

    Action (PhysicsLogic) Environment Environment Environment ֤αϯϓϧը໘ͷϩδοΫ
  22. (AppLogic) Environment Counter Environment Downloader Environment Physics Environment Reducer Counter

    Reducer Downloader Reducer Physics Reducer State Counter State Downloader State Physics State Action Counter Action Downloader Action Physics Action "DUJPO 4UBUF 3FEVDFS &OWJSPONFOU୯ҐͰ߹੒
  23. Reducer State Action AppView Store (AppLogic) ΞϓϦͷશͯΛ໢ཏ͢Δ 
 ܕͱϩδοΫ Reducer

    State Action Environment Environment 4JOHMF4PVSDF PG5SVUIͳ །Ұਆͷ஀ੜ
  24. CounterView (Computed) Store Counter Reducer Counter State Counter Action Counter

    Environment AppView Store App Reducer App State App Action App Environment ֤ը໘͸ ΞϓϦશମͷ ৘ใΛ஌Βͳ͍ ૄ݁߹ ࢠཁૉʹม׵ 4JOHMF 4PVSDF PG5SVUI 4UPSF෼ղ ೿ੜ4UPSF ݩͷ4UPSF͔Β ࢉग़ͨ͠΋ͷ
  25. CounterView (Child)Store Counter Reducer Counter State Counter Action Counter Environment

    AppView Store App Reducer App State App Action App Environment ֤ը໘͸ ΞϓϦશମͷ ৘ใΛ஌Βͳ͍ ࢠཁૉʹม׵ 4JOHMF 4PVSDF PG5SVUI 4UPSF෼ղ ΦϒδΣΫτ߹੒ͷ୅ΘΓʹ ؔ਺ (ܕͱReducer) Λ߹੒ Single Source of Truth ͳ 
 ΦϒδΣΫτΛ֤ը໘ʹ෼ղ͢Δ
  26. // App.Action enum Action: Sendable { case changeCurrent(State.Current?) // NavigationView

    ੾Γସ͑࣌ʹൃՐ case counter(Counter.Action) case gameOfLife(GameOfLife.Root.Action) case physics(PhysicsRoot.Action) case downloader(Downloader.Action) ... } // App.State struct State: Equatable, Sendable { var current: Current? // ݱࡏදࣔதͷը໘ʢnil ͷ৔߹ɺHome ը໘ʣ enum Current: Equatable, Sendable { case counter(Counter.State) case gameOfLife(GameOfLife.Root.State) case physics(PhysicsRoot.State) case downloader(Downloader.State) ... } } ֤ը໘ͷΞΫγϣϯΛ શମͷΞΫγϣϯʹ߹੒ ֤ը໘ͷঢ়ଶΛ શମͷঢ়ଶʹ߹੒ 
 /05&֤ը໘ͷঢ়ଶ͸ 
 ಉ࣌ʹଘࡏ͠ͳ͍ͷͰ FOVNͰఆٛ͢Δ
  27. let appReducer = Reducer<Action, State, Environment>.combine( Counter.reducer .contramap(action: /Action.counter) .contramap(state:

    /State.Current.counter) .contramap(state: \State.current) .contramap(environment: { _ in () }), GameOfLife.Root.reducer() .contramap(action: /Action.gameOfLife) .contramap(state: /State.Current.gameOfLife) .contramap(state: \State.current) .contramap(environment: { $0.gameOfLife }), PhysicsRoot.reducer .contramap(action: /Action.physics) .contramap(state: /State.Current.physics) .contramap(state: \State.current) .contramap(environment: { .init(timer: $0.timer) }), Downloader.reducer .contramap(action: /Action.downloader) .contramap(state: /State.Current.downloader) .contramap(state: \State.current) .contramap(environment: \.downloader), ... // ҎԼɺ߹੒͍ͨ͠ը໘ͷ਺͚ͩม׵ίʔυΛॻ͘ ) ֤ը໘ͷ3FEVDFSΛ ࢠը໘͔Β਌ը໘ʹ޲͚ͯ ม׵ܭࢉͯ݁͠߹͢Δ 5$"ͷQVMMCBDL  ؔ਺ܕϓϩάϥϛϯάʹ͓͚Δ 
 ൓มؔख DPOUSBNBQ  -FOT 1SJTNΛ࢖͍ͬͯΔ J04%$ϓϨθϯࢀর
  28. @MainActor struct AppView: View { let store: Store<Action, State, Environment>

    var body: some View { NavigationView { WithViewStore(store.map(state: \.common)) { viewStore in List(exampleList, id: \.exampleTitle) { example in navigationLink(example: example) } } } } func navigationLink(example: Example) -> some View { WithViewStore(store.map(state: \.current)) { viewStore in NavigationLink( destination: example.exampleView(store: self.store), isActive: viewStore .binding(onChange: Action.changeCurrent) .transform(...) ) { ... } } } } TUPSFNBQͰࢠ DPNQVUFE 4UPSFΛ࡞੒ 5$"ͷ4UPSFTDPQF
  29. ViewController ViewModel VC Builder Router UseCase APIClient func showNext State

    VC Builder Shows next ViewController Owns Weakly references Calls Builds #VJMEFS6TF$BTF3PVUFSύλʔϯ
  30. // ैདྷઃܭɿ ViewController, ViewModel, UseCase, Router Λͭͳ͗͜ΉϏϧμʔํࣜ func build(apiClient: any

    APIClientProtocol) -> UIViewController { let useCase = UseCase(apiClient: apiClient) let viewModel = ViewModel(useCase: useCase) let viewController = ViewController(viewModel: viewModel) let router = Router(viewController: viewController) viewController.router = router return viewController } // ViewController ͕ݺͼग़͢ϧʔςΟϯάॲཧ struct Router: RouterProtocol { weak var viewController: UIViewController? func handleRoute1(...) { ... } // ը໘ભҠύλʔϯ1 func handleRoute2(...) { ... } // ը໘ભҠύλʔϯ2 }
  31. ViewController RouteStore VC Builder Router (closure) Environment fetch Reducer State

    Action Route Publisher VC Builder Shows next ViewController Sends Route Weakly references Builds 3PVUF4UPSF JO"DUPNBUPO
  32. // Actomaton RouteStore Λ࢖ͬͨ Environment, Store, Router ͷͭͳ͗͜Έ func build(apiClient:

    any APIClientProtocol) -> UIViewController { let environment = Environment(fetch: { request in return apiClient.fetch(request) }) let store = RouteStore<Action, State, Environment, Route>( state: State(), reducer: reducer environment: environment ) let viewController = ViewController(store: store) store.subscribeRoutes { [weak viewController] route in ... } // ը໘ભҠॲཧ return viewController } // ը໘ભҠύλʔϯͷܕΛ༻ҙ͢Δ enum Route { case route1(...) case route2(...) } ϧʔςΟϯάॲཧ͕Ͱ͖Δ 3PVUF4UPSFΛ࢖͏ &OWJSPONFOU͸ 
 6TF$BTFͱಉ͡ %*Մೳ
  33. let reducer = Reducer { action, state, environment in switch

    action { case .fetch: state.isLoading = true return Effect(queue: FetchEffectQueue()) { let response = try await environment.fetch(Request(...)) return Action._didFetch(response) } case let ._didFetch(response): state.isLoading = false return Effect.fireAndForget { print(response) } case .showNext: return Effect.fireAndForget { environment.sendRoute(Route.route1(...)) } } } 3FEVDFSͷதͷFOJWPSONFOU͕ TFOE3PVUFΛ࢖͏͜ͱ͕Ͱ͖Δ
  34. Action 􀉭 Real View Reducer State Virtual View Effect 💥

    Runtime Action 􀉭 Real View Reducer State Virtual View Effect 💥 Runtime Screen 1 Screen 2 ֤ը໘͝ͱʹ 
 &MN"SDIJUFDUVSF3VOUJNF 
 3PVUF4UPSF Λ༻ҙ͢Δ
  35. Action 􀉭 Real View Reducer State Virtual View Effect 💥

    Runtime Action 􀉭 Real View Reducer State Virtual View Effect 💥 Runtime Screen 2 Shows next ViewController + Messaging forward Messaging backward sendRoute sendRoute ը໘ؒͷσʔλͷ΍ΓऔΓ ը໘ભҠΛؚΉ  ΛTFOE3PVUFΛ࢖࣮ͬͯߦ
  36. Msg (Action) 􀉭 Real View Update (Reducer) State Virtual View

    view (render) Diff (New State) User Action Cmd (Effect) 💥 Runtime I/O Run Side-effect
  37. Msg (Action) 􀉭 Real View Update (Reducer) State Virtual View

    view (render) Diff (New State) User Action Cmd (Effect) 💥 Runtime I/O Run Side-effect ؔ਺ܕϓϩάϥϛϯά
  38. Msg (Action) 􀉭 Real View Update (Reducer) State Virtual View

    view (render) Diff (New State) User Action Cmd (Effect) 💥 Runtime I/O Run Side-effect ΦϒδΣΫτࢦ޲ ϓϩάϥϛϯά
  39. ϜʔΞɾϚγϯ&MN"SDIJUFDUVSF indirect enum Moore<I, A> { case runMoore(A, I ->

    Moore<I, A>) } Moore<Action, View> ≅ (View, Action -> Moore<Action, View>) ≅ νX. (View, Action -> X) // ࠷େෆಈ఺ νX.F<X> = ∃S. (S, (S -> F<S>)) ≅ ∃S. (S, (S -> (View, Action -> S))) ≅ ∃S. (S, S -> View, S -> Action -> S) ≅ ∃S. (initialState: S, reducer: S -> Action -> S, render: S -> View) ϜʔΞɾϚγϯ ೖྗʹґଘ͠ͳ͍ঢ়ଶػց ৗʹग़ྗՄೳͳ"ͱ ೖྗ*͔Βࣗ਎΁ͷঢ়ଶભҠͷ 
 ϖΞΛ༨ؼೲతʹఆٛͨ͠ܕ J04%$ϓϨθϯࢀর
  40. Msg (Action) 􀉭 Real View Update (Reducer) State Virtual View

    view (render) Diff (New State) User Action Cmd (Effect) 💥 Runtime I/O Run Side-effect Elm Architecture = ॳظঢ়ଶ × Reducer × render
  41. Msg (Action) 􀉭 Real View Update (Reducer) State Virtual View

    view (render) Diff (New State) User Action Cmd (Effect) 💥 Runtime I/O Run Side-effect
  42. Msg (Action) 􀉭 Real View Update (Reducer) State Virtual View

    view (render) Diff (New State) User Action Cmd (Effect) 💥 Runtime I/O Run Side-effect Comonad ʹΑΔ ؔ਺ܕϓϩάϥϛϯά
  43. Msg (Action) 􀉭 Real View Update (Reducer) State Virtual View

    view (render) Diff (New State) User Action Cmd (Effect) 💥 Runtime I/O Run Side-effect ΞΫλʔϞσϧ (ࢀর) ʹΑΔ ΦϒδΣΫτࢦ޲ ϓϩάϥϛϯά
  44. ༨ஊ ϛʔϦɾϚγϯΛ࢖ͬͨΦϒδΣΫτࢦ޲ͷղऍ wDG)BTLFMMͰͷ߹੒ՄೳͳΦϒδΣΫτͷߏ੒ͱͦͷԠ༻ ໦ԼҮষ ࢁຊ࿨඙   w0CKFDU͸ʮܭࢉޮՌͷݍʹ͓͚Δࣹʯͱͯ͠ѻ͑Δ ߹੒Մೳ 

    wΧϓηϧԽɾܧঝɾΦʔόʔϥΠυΛ࣮ݱ͢Δ indirect enum Mealy<I, A> { case runMealy(I -> (A, Mealy<I, A>)) } ϛʔϦɾϚγϯ ೖྗ*͕൐ͬͯ ग़ྗ"ͱࣗ਎΁ͷঢ়ଶભҠ Λฦ͢ɺ༨ؼೲతʹఆٛͨ͠ܕ indirect enum Object<Msg> { case runObject<A>(Msg<A> -> IO<(A, Object<Msg>)>) } .TH͔Β*0΁ͷࣗવม׵
  45. ༨ஊ ϛʔϦɾϚγϯΛ࢖ͬͨΦϒδΣΫτࢦ޲ͷղऍ wDG)BTLFMMͰͷ߹੒ՄೳͳΦϒδΣΫτͷߏ੒ͱͦͷԠ༻ w0CKFDU͸ʮܭࢉޮՌͷݍʹ͓͚Δࣹʯͱͯ͠ѻ͑Δ ߹੒Մೳ  wΧϓηϧԽɾܧঝɾΦʔόʔϥΠυΛ࠶ݱ͢Δ indirect enum Mealy<I,

    A> { case runMealy(I -> (A, Mealy<I, A>)) } ϛʔϦɾϚγϯ ೖྗ*͕൐ͬͯ ग़ྗ"ͱࣗ਎΁ͷঢ়ଶભҠ Λฦ͢ɺ༨ؼೲతʹఆٛͨ͠ܕ indirect enum Object<Msg> { case runObject<A>(Msg<A> -> IO<(A, Object<Msg>)>) } .TH͔Β*0΁ͷࣗવม׵ ؔ਺ܕϓϩάϥϛϯά͔Β ΦϒδΣΫτࢦ޲΁ ʙ ༨ؼೲɾ༨୅਺ɾঢ়ଶػց ʙ