Reactive State Machine (Japanese)

Reactive State Machine (Japanese)

Eac0bf787b5279aca5e699ece096956e?s=128

Yasuhiro Inami

August 20, 2016
Tweet

Transcript

  1. Reactive State Machine 2016/08/20 #iOSDC Japan Yasuhiro Inami / @inamiy

  2. ϦΞΫςΟϒ ϓϩάϥϛϯά

  3. None
  4. ࣌ؒܦաʹ൐͏ ΠϕϯτετϦʔϜ

  5. Swift FRP༻ϥΠϒϥϦ (FRP = Functional Reactive Programming) • RxSwift •

    ReactiveCocoa (RAC) • SwiftBond / ReactiveKit • Interstellar • ReactKit ؔ਺ܕϓϩάϥϛϯάͰɺએݴతʹɺσʔλϑϩʔΛߏங͢Δ
  6. !Λͭ͘Δ !͕ྲྀΕΔ

  7. !ΛͲ͏͢Δʁ ৯΂Δ εϧʔ

  8. !ΛҭͯΔ

  9. !ͷ؅ཧ

  10. ঢ়ଶͷมߋʢData Bindingʣ final class ViewModel<!> { var rawValue: ! let

    variable: RxSwift.Variable<!> let mutableProperty: RAC.MutableProperty<!> } mySuperEventEmitter.on { viewModel.rawValue = $0 } observable.bindTo(viewModel.variable) // RxSwift viewModel.mutableProperty <~ signal // ReactiveCocoa
  11. ঢ়ଶͷऔಘʢData Observingʣ final class ViewModel<!> { var rawValue: ! let

    variable: RxSwift.Variable<!> let mutableProperty: RAC.MutableProperty<!> } // `viewModel.rawValue`͸؂ࢹͰ͖ͳ͍ʂ (KVOͰͳ͍ݶΓ) viewModel.variable.asObservable().doOn {...} // RxSwift viewModel.mutableProperty.signal.on {...} // ReactiveCocoa
  12. Observable or Signal ≒ EventEmitter

  13. Variable or MutableProperty ≒ EventEmitter + ݱࡏͷ஋

  14. FRP + MVVMͰ ঢ়ଶ؅ཧ͕ ͱͬͯ΋؆୯ʂ

  15. None
  16. ຊ౰ʹʁ

  17. (Swift߃ྫ) ϙέϞϯʹྫ͑ͯΈΔ

  18. final class ViewModel<T> { let variable: Variable<T> }

  19. final class ViewModel<T> { let variable: Variable<T> } final class

    ViewModel2<T> { let variable: Variable<T> }
  20. final class ViewModel<T> { let variable: Variable<T> } final class

    ViewModel2<T> { let variable: Variable<T> } final class ViewModel3<T> { let variable: Variable<T> }
  21. final class ViewModel<T> { let variable: Variable<T> } final class

    ViewModel2<T> { let variable: Variable<T> } final class ViewModel3<T> { let variable: Variable<T> } ... final class ViewModel1000<T> { let variable: Variable<T> }
  22. let variable = { n in viewModel[n].variable } variable(1).asObservable() .map

    { ... } .bindTo(variable(2)) variable(2).variable.asObservable() .flatMap { ... } .bindTo(variable(3)) Observable.combineLatest(variable(123), variable(456)) .map { ... } .bindTo(variable(789)) ...
  23. None
  24. None
  25. None
  26. 1000ݸͷViewModel ͕৫Γͳ͢ ૖େͳεϖΫλΫϧ

  27. ఺ࡏͨ͠ແ਺ͷঢ়ଶ ܅͸খӉ஦Λײͨ͜͡ͱ͕͋Δ͔

  28. FRP + MVVM ◦ ϑϩʔߏங × ঢ়ଶ؅ཧ

  29. FRP + MVVMͷ໰୊఺(1) // RxSwiftͰUI binding viewModel.variable.asDriver().drive(/* UIߋ৽ */) //

    1. ೚ҙͷλΠϛϯάͰɺ͔ͭόϦσʔγϣϯͳ͠ʹɺ // ঢ়ଶมߋ͕Մೳ ! ʮՄมͳঢ়ଶʯ viewModel.variable.value = "!!! poops!" // ઒ΛԚ͢ਓ͕ݱΕΔ
  30. final class ViewModel<T> { var rawValue: T let variable: Variable<T>

    } let variable͸ var rawValueͱ େͯ͠มΘΒͳ͔ͬͨ ΜͩΑʂʂʂ
  31. FRP + MVVMͷ໰୊఺(2) // RxSwiftͰUI binding viewModel.variable.asDriver().drive(/* UIߋ৽ */) //

    2. ೚ҙͷλΠϛϯάͰଞͷϑϩʔͱͭͳ͙ɾ֎͢͜ͱ͕Մೳ // ! ʮՄมͳσʔλϑϩʔʯ let disposable = !!!Observable .bindTo(viewModel.variable) // ͭͳ͙ disposable.dispose() // ֎͢
  32. ʮσʔλϑϩʔʯ ͦͷ΋ͷ͕ ʮঢ়ଶʯ ͩͬͨΜͩ ʔʔʔʂʂʂ

  33. FRP + MVVM ↓ ঢ়ଶʮૢ࡞ʯ͸ָʹͳͬͨ ↓ ͕ɺঢ়ଶͷ਺͸Ή͠Ζ૿͍͑ͯΔ

  34. ຊ౰ʹঢ়ଶʮ؅ཧʯ ग़དྷͯΔʁ

  35. !

  36. ໨·͙Δ͘͠มಈ͢Δ ʮσʔλϑϩʔʯ ༧ଌͰ͖ͳ͍ແ਺ͷ ʮঢ়ଶʯ

  37. ༧ଌՄೳͳʮঢ়ଶʯ Model-View-???

  38. ൃ૝ͷస׵ σʔλϑϩʔͷݻఆ ঢ়ଶͷ਺Λ࠷খԽ

  39. React.js

  40. React.js • ViewΛ(ͳΔ΂͘)εςʔτϨεʹѻ͏ϑϨʔϜϫʔΫ • Component: ੜͷDOMͷ୅ΘΓʹϨϯμϦϯά • JSXΛ࢖ͬͯɺXMLϥΠΫʹπϦʔߏ଄Λهड़ • Componentͷঢ়ଶΛߋ৽͢Δͱɺࠩ෼ߋ৽

    (Virtual DOM) • ਌͔Βࢠ΁ͷσʔλϑϩʔ • (ڱٛͷ)ϦΞΫςΟϒϓϩάϥϛϯάͱ͸ผ෺
  41. None
  42. None
  43. React.jsͷ೉఺ ࢠ͔Β਌΁ͷ σʔλϑϩʔ͕ਏ͍

  44. Redux

  45. None
  46. None
  47. Redux • ΞϓϦͷશͯͷঢ়ଶΛɺ1ͭͷίϯςφ (Store) Ͱ؅ཧ • ΞϓϦ͸Ͱ͔͍ͬγϯάϧτϯ • ࠷্෦ͷ਌Component΋ঢ়ଶ؅ཧ͔Βղ์͞ΕΔ •

    Ͳͷ֊૚ͷComponent͔ΒͰ΋ɺίϯςφʹActionΛૹΔ͜ ͱ͕؆୯ • ࢠ͔Β਌΁ͷσʔλϑϩʔ
  48. None
  49. ໰୊఺΋͋Δ

  50. Reduxͷ໰୊఺ • MiddlewareͷҐஔ෇͚ • ReducerΛ௨Δલʹ෭࡞༻(IO)Λѻ ͏ • ReducerΛ௨Γͳ͕Β෭࡞༻Λ ॲཧ͍ͨ͠৔߹͸ʁ •

    ີ݁߹͕ඞཁͳέʔε͸ଟ͍ • ॱংґଘ
  51. Reduxͷ໰୊఺ • ඇಉظॲཧ͕೉͍͠ • MiddlewareͷதʹThunk? Promise? ES6 Generator? Observable? •

    Q. ෳ਺ͷMiddleware͕ඇಉظͰ࿈࠯൓Ԡͨ͠ΒͲ͏ͳ Δʁ!"!"!"! • A. ਏ͍ɻඇಉظॲཧνΣʔϯͳΒɺObservableͷ࿈݁ʹ ೚ͤͨํ͕෼͔Γ΍͍͢
  52. ΋ͬͱྑ͍ઃܭΛ ߟ͑Δ!

  53. ReduxͷܕΛோΊΔ • Action = ೖྗɺState = ঢ়ଶ • Store =

    ঢ়ଶίϯςφ • Reducer = (State, Action) -> State = ঢ়ଶભҠؔ਺ • Listener = State -> /* IO */ () = ঢ়ଶมߋΦϒβʔόʔ • Middleware = State -> ... -> Action -> /* IO */ Any = ෭࡞༻ϑοΫ
  54. None
  55. εςʔτϚγϯ • ܾఆੑʗඇܾఆੑɺ༗ݶʗແݶ • ΞΫηϓλʔʗϨίφΠβʔ • ϓογϡμ΢ϯɺνϡʔϦϯάɾϚγϯͳͲ • τϥϯεσϡʔαʔ •

    ϜʔΞɾϚγϯ (1956) • ϛʔϦɾϚγϯ (1955)
  56. ϛʔϦɾϚγϯ (Σ, Ω, S, s0, δ, λ)

  57. ϛʔϦɾϚγϯ ʮݱࡏͷঢ়ଶʯͱʮ֎෦ೖྗʯ͔Βɺʮ৽͍͠ঢ়ଶʯͱʮग़ ྗʯΛੜΈग़͢༗ݶτϥϯεσϡʔαʔ

  58. ϛʔϦɾϚγϯ • Σ ͸ʮೖྗͷू߹ʯ • Ω ͸ʮग़ྗͷू߹ʯ • S ͸ʮঢ়ଶͷू߹ʯ

    • s0 ͸ʮॳظঢ়ଶʯʢs0 ∈ Sʣ • δ ͸ʮঢ়ଶભҠؔ਺ʯ δ: S x Σ → S • λ ͸ʮग़ྗؔ਺ʯ λ: S x Σ → Ω
  59. Redux = ϛʔϦɾϚγϯ • Action = ೖྗ • State =

    ঢ়ଶ • Store = ॳظঢ়ଶʴঢ়ଶભҠؔ਺ • Reducer = ঢ়ଶભҠؔ਺ • Listener = ग़ྗ (IO) • Middleware = ग़ྗؔ਺
  60. 1955೥͔Β͋ͬͨ

  61. ͔࣌͠͠͸ɺ2016೥ ੈք͸FRPͷԌͰͭͭ·Εͨ

  62. Redux + FRP

  63. Redux + FRPʢվળҊʣ 1. ReducerͱMiddlewareΛ౷߹͢Δ: S x Σ -> S

    x Ω 2. ReducerͷฦΓ஋ΛOptionalʹ͢Δ • ঢ়ଶมԽ͕ͳ͍৔߹(ભҠࣦഊ)ɺෆཁͳdispatchΛආ͚Δ 3. FRP (ReactiveCocoa)Λ࢖͏ʢඇಉظνΣʔϯͷվળʣ 4. ग़ྗͷܕΛIO͔ΒɺʮIOʴ࣍ͷೖྗ΋ૹ৴Ͱ͖Δʯ ܕ SignalProducer<Input, NoError> Λ࢖͏
  64. Reactive / Rx Automaton inamiy/ReactiveAutomaton inamiy/RxAutomaton

  65. Redux ˰ ReactiveAutomaton • Store 㱺 Automaton • Action 㱺

    Input • Reducer 㱺 (State, Input) -> (State, SignalProducer<Input, NoError>)? • Listener 㱺 Reply<State, Input> -> /* IO */ () • Middleware 㱺 !
  66. ࢖༻ྫ

  67. αϯϓϧίʔυʢϩάΠϯॲཧʣ • State: LoggedOut, LoggingIn, LoggedIn, LoggingOut • Input: Login,

    LoginOK, Logout, LogoutOK, ForceLogout
  68. αϯϓϧίʔυʢϩάΠϯॲཧʣ // 1. switch-caseΛ࢖ͬͨύλʔϯϚονϯά let mapping: NextMapping = { fromState,

    input in switch (fromState, input) { case (.LoggedOut, .Login): return (.LoggingIn, loginOKProducer) case (.LoggingIn, .LoginOK): return (.LoggedIn, .empty) case (.LoggedIn, .Logout): return (.LoggingOut, logoutOKProducer) case (.LoggingOut, .LogoutOK): return (.LoggedOut, .empty) case (.LoggingIn, .ForceLogout), (.LoggedIn, .ForceLogout): return (.LoggingOut, forceLogoutOKProducer) default: return nil } }
  69. αϯϓϧίʔυʢϩάΠϯॲཧʣ let canForceLogout: State -> Bool = [.LoggingIn, .LoggedIn].contains //

    2. ΧελϜԋࢉࢠΛ࢖ͬͨύλʔϯϚονϯά let mappings: [NextMapping] = [ /* input | fromState => toState | effect */ /* ---------------------------------------------------------- */ .Login | .LoggedOut => .LoggingIn | loginOKProducer, .LoginOK | .LoggingIn => .LoggedIn | .empty, .Logout | .LoggedIn => .LoggingOut | logoutOKProducer, .LogoutOK | .LoggingOut => .LoggedOut | .empty, .ForceLogout | canForceLogout => .LoggingOut | forceLogoutOKProducer ]
  70. αϯϓϧίʔυʢϩάΠϯॲཧʣ let (inputSignal, observer) = Signal<Input, NoError>.pipe() let send =

    observer.sendNext let automaton = Automaton( state: .LoggedOut, input: inputSignal, mapping: reduce(mappings), // combining reducers strategy: .Latest ) automaton.replies.observeNext { print($0) }
  71. αϯϓϧίʔυʢϩάΠϯॲཧʣ expect(automaton.state.value) == .LoggedIn // ϩάΠϯࡁΈ send(Input.Logout) expect(automaton.state.value) == .LoggingOut

    // ϩάΞ΢τதɾɾɾ // `logoutOKProducer`ʹΑΓɺࣗಈతʹϩάΞ΢τ׬ྃʹભҠ expect(automaton.state.value) == .LoggedOut // ϩάΞ΢τࡁΈ send(Input.Login) expect(automaton.state.value) == .LoggingIn // ϩάΠϯதɾɾɾ // ͦͷ··์͓͚ͬͯ͹ɺࣗಈతʹϩάΠϯ׬ྃʹͳΔ͕ɾɾɾ send(Input.ForceLogout) // ڧ੍ϩάΞ΢τൃಈ!"! expect(automaton.state.value) == .LoggingOut // ϩάΞ΢τதɾɾɾ // `forceLogoutOKProducer`ʹΑΓɺࣗಈతʹϩάΞ΢τ׬ྃʹભҠ
  72. None
  73. ReactiveAutomaton • খ͞ͳঢ়ଶ؅ཧ͔ΒͰ͔͍ͬγϯάϧτϯ·Ͱɺ෯޿͘ରԠ • Elmͷجຊઃܭʹ͍ۙ • Program + (ݴޠ಺) Effect

    Manager = ΦʔτϚτϯ • Model = ঢ়ଶɺMsg = ೖྗ • update : Msg -> Model -> (Model, Cmd Msg) = ঢ়ଶ ભҠʴIOग़ྗؔ਺
  74. None
  75. Model View ViewModel

  76. Model View ✨Automaton✨

  77. ·ͱΊ • ʮσʔλϑϩʔʯͱ͍͏໊ͷʮঢ়ଶʯ • FRP + MVVMʹΑΔঢ়ଶ؅ཧ͸ґવͱͯ͠ෳࡶ • React.js +

    Redux͔ΒֶͿ • UIKitͷਐԽ΋ඞཁʢReactNativeͳͲʣ • Redux + FRP = Reactive State Machine • Elm͸ཁνΣοΫʢࢀߟɿ A Farewell to FRPʣ
  78. Thanks!