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

Reactive State Machine (Japanese)

Reactive State Machine (Japanese)

Yasuhiro Inami

August 20, 2016
Tweet

More Decks by Yasuhiro Inami

Other Decks in Programming

Transcript

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

    ReactiveCocoa (RAC) • SwiftBond / ReactiveKit • Interstellar • ReactKit ؔ਺ܕϓϩάϥϛϯάͰɺએݴతʹɺσʔλϑϩʔΛߏங͢Δ
  2. ঢ়ଶͷมߋʢ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
  3. ঢ়ଶͷऔಘʢ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
  4. final class ViewModel<T> { let variable: Variable<T> } final class

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

    ViewModel2<T> { let variable: Variable<T> } final class ViewModel3<T> { let variable: Variable<T> }
  6. 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> }
  7. 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)) ...
  8. FRP + MVVMͷ໰୊఺(1) // RxSwiftͰUI binding viewModel.variable.asDriver().drive(/* UIߋ৽ */) //

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

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

    2. ೚ҙͷλΠϛϯάͰଞͷϑϩʔͱͭͳ͙ɾ֎͢͜ͱ͕Մೳ // ! ʮՄมͳσʔλϑϩʔʯ let disposable = !!!Observable .bindTo(viewModel.variable) // ͭͳ͙ disposable.dispose() // ֎͢
  11. !

  12. Reduxͷ໰୊఺ • ඇಉظॲཧ͕೉͍͠ • MiddlewareͷதʹThunk? Promise? ES6 Generator? Observable? •

    Q. ෳ਺ͷMiddleware͕ඇಉظͰ࿈࠯൓Ԡͨ͠ΒͲ͏ͳ Δʁ!"!"!"! • A. ਏ͍ɻඇಉظॲཧνΣʔϯͳΒɺObservableͷ࿈݁ʹ ೚ͤͨํ͕෼͔Γ΍͍͢
  13. ReduxͷܕΛோΊΔ • Action = ೖྗɺState = ঢ়ଶ • Store =

    ঢ়ଶίϯςφ • Reducer = (State, Action) -> State = ঢ়ଶભҠؔ਺ • Listener = State -> /* IO */ () = ঢ়ଶมߋΦϒβʔόʔ • Middleware = State -> ... -> Action -> /* IO */ Any = ෭࡞༻ϑοΫ
  14. ϛʔϦɾϚγϯ • Σ ͸ʮೖྗͷू߹ʯ • Ω ͸ʮग़ྗͷू߹ʯ • S ͸ʮঢ়ଶͷू߹ʯ

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

    ঢ়ଶ • Store = ॳظঢ়ଶʴঢ়ଶભҠؔ਺ • Reducer = ঢ়ଶભҠؔ਺ • Listener = ग़ྗ (IO) • Middleware = ग़ྗؔ਺
  16. Redux + FRPʢվળҊʣ 1. ReducerͱMiddlewareΛ౷߹͢Δ: S x Σ -> S

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

    Input • Reducer 㱺 (State, Input) -> (State, SignalProducer<Input, NoError>)? • Listener 㱺 Reply<State, Input> -> /* IO */ () • Middleware 㱺 !
  18. αϯϓϧίʔυʢϩάΠϯॲཧʣ // 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 } }
  19. αϯϓϧίʔυʢϩάΠϯॲཧʣ 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 ]
  20. αϯϓϧίʔυʢϩάΠϯॲཧʣ 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) }
  21. αϯϓϧίʔυʢϩάΠϯॲཧʣ 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`ʹΑΓɺࣗಈతʹϩάΞ΢τ׬ྃʹભҠ
  22. ReactiveAutomaton • খ͞ͳঢ়ଶ؅ཧ͔ΒͰ͔͍ͬγϯάϧτϯ·Ͱɺ෯޿͘ରԠ • Elmͷجຊઃܭʹ͍ۙ • Program + (ݴޠ಺) Effect

    Manager = ΦʔτϚτϯ • Model = ঢ়ଶɺMsg = ೖྗ • update : Msg -> Model -> (Model, Cmd Msg) = ঢ়ଶ ભҠʴIOग़ྗؔ਺
  23. ·ͱΊ • ʮσʔλϑϩʔʯͱ͍͏໊ͷʮঢ়ଶʯ • FRP + MVVMʹΑΔঢ়ଶ؅ཧ͸ґવͱͯ͠ෳࡶ • React.js +

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