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

Reactive State Machine (Japanese)

Reactive State Machine (Japanese)

Avatar for Yasuhiro Inami

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ʣ