Orakaro
October 10, 2017
16k

October 10, 2017

Transcript

Talk

ΦʔτϚτϯ
4. εςʔτϚγϯͷ͓͞Β͍ • ༗ݶΦʔτϚτϯ • ભҠؔ਺ͷΈ (s, a) -> s •

Mealy Machine • ભҠؔ਺ (s, a) -> s • ग़ྗؔ਺ (s, a) -> b • Moore Machine • ભҠؔ਺ (s, a) -> s • ग़ྗؔ਺ s -> b
5. ༗ݶΦʔτϚτϯ • ༗ݶΦʔτϚτϯ • ભҠؔ਺ͷΈ (s, a) -> s •

Mealy Machine • ભҠؔ਺ (s, a) -> s • ग़ྗؔ਺ (s, a) -> b • Moore Machine • ભҠؔ਺ (s, a) -> s • ग़ྗؔ਺ s -> b ঢ়ଶΛ؅ཧ͍ͨ࣌͠ʹ׆༻͢΂͖
6. Binary Gap A binary gap within a positive integer N

is any maximal sequence of consecutive zeros that is surrounded by ones at both ends in the binary representation of N. Ex: 9 = 1001 → 2
7. A binary gap within a positive integer N is any

maximal sequence of consecutive zeros that is surrounded by ones at both ends in the binary representation of N. Ex: 9 = 1001 → 2 Started Zero One Binary Gap
8. Visual Format Language class func constraints(withVisualFormat format: String, options opts:

NSLayoutFormatOptions = [], metrics: [String : Any]?, views: [String : Any]) -> [NSLayoutConstraint] "H:|-15-[iconImageView(30)]-[appNameLabel]-[skipButton]-15-|"
9. Visual Format Language class func constraints(withVisualFormat format: String, options opts:

NSLayoutFormatOptions = [], metrics: [String : Any]?, views: [String : Any]) -> [NSLayoutConstraint] "H:|-15-[iconImageView(30)]-[appNameLabel]-[skipButton]-15-|" Direction Start Line Number Label [ ] ( ) End

16. ϝʔϧϩάΠϯ

Email Login Register With Mercari Conﬁrm SMS Home PassCode Email SignUp Reset Password

20. ΋͸΍֤ը໘͕ εςʔτΛදݱ͍ͯ͠Δͷ͡Ό

Resource Mercari Resource
22. RegistrationStateViewController protocol RegistrationStateViewController { var state: RegistrationState { get }

func nextViewController(event: RegistrationEvent) -> UIViewController? } extension RegistrationStateViewController { func nextViewController(event: RegistrationEvent) -> UIViewController? { let machine = AutomatonManager.registrationMachine guard let state = machine.transition(from: state, by: event) else { return nil } switch state { //... } } ΦʔτϚτϯ
23. TransitionΛఆٛ struct Transition<S, E> { let from: S let to:

S let by: E } from to by State State Event
24. State MachineΛ࣮૷ class Automaton<S: Hashable, E: Hashable> { var routes:

[S: [E: S]] = [:] init(initialState: S, transitions: [Transition<S, E>]) { for t in transitions { addRoute(t) } } private func addRoute(_ t: Transition<S, E>) { var dict = routes[t.from] ?? [:] dict[t.by] = t.to routes[t.from] = dict } func transition(from: S, by: E) -> S? { guard let next = routes[from].flatMap({ \$0[by] }) else { return nil } return next } }
25. State MachineΛ࣮૷ class Automaton<S: Hashable, E: Hashable> { var routes:

[S: [E: S]] = [:] init(initialState: S, transitions: [Transition<S, E>]) { for t in transitions { addRoute(t) } } private func addRoute(_ t: Transition<S, E>) { var dict = routes[t.from] ?? [:] dict[t.by] = t.to routes[t.from] = dict } func transition(from: S, by: E) -> S? { guard let next = routes[from].flatMap({ \$0[by] }) else { return nil } return next } } ࣙॻܕΛ࢖ͬͯભҠΛ؅ཧ͢Δ

27. ભҠάϥϑΛఆٛ let registrationGraph: [Transition<RegistrationState, RegistrationEvent>] = [ Transition(from: .root, to:

.registerRoot, by: DefaultEvent.showRegisterRoot), Transition(from: .registerRoot, to: .profileRegister, by: DefaultEvent.registerWithFacebook), Transition(from: .registerRoot, to: .startWithMercari, by: DefaultEvent.startWithMercari), Transition(from: .registerRoot, to: .emailLogin, by: DefaultEvent.loginWithEmail), Transition(from: .registerRoot, to: .atteLogin, by: DefaultEvent.loginWithAtte), Transition(from: .registerRoot, to: .home, by: DefaultEvent.loginAndShowHome), ] let registrationMachine = Automaton<RegistrationState, RegistrationEvent>( initialState: .root, transitions: registrationGraph )
28. ભҠϩδοΫ͚ͩςετͰ͖Δ describe("ΞϓϦىಈ࣌τʔΫϯ͕ଘࡏ͠ͳ͍") { it("ϝʔϧͰ৽نొ࿥͕Ͱ͖Δ") { self.tryRoute(events: [ DefaultEvent.showRegisterRoot, DefaultEvent.registerWithEmail, DefaultEvent.showProfileRegister,

DefaultEvent.confirmSMS, DefaultEvent.loginAndShowHome ]) XCTAssert(self.currentState == .home) } } //... private func tryRoute(events: [RegistrationEvent]) { for event in events { self.currentState.map { state in self.currentState = self.machine.transition(from: state, by: event) } } }

30. RegistrationStateViewController protocol RegistrationStateViewController { var state: RegistrationState { get }

func nextViewController(event: RegistrationEvent) -> UIViewController? } extension RegistrationStateViewController { func nextViewController(event: RegistrationEvent) -> UIViewController? { let machine = AutomatonManager.registrationMachine guard let state = machine.transition(from: state, by: event) else { return nil } switch state { //... } } ͜͜ͰViewControllerΛੜ੒͢Δͷʁ
31. RegistrationStateViewController switch state { case .root: return RootViewController.make(withDependency: ()) case

.registerRoot: return RegisterRootViewController.make(withDependency: ()) case .profileRegister : switch event { case .registerWithFacebook(.some(let profile)): return ProfileRegisterViewController.make(withDependency: .init(facebookProfile: profile)) default: return nil } case .startWithMercari: switch event { case .startWithMercari(.some(let resource), .some(let token)): return StartWithMercariViewController.make(withDependency: .init(resource: resource, token: token)) default: return nil } case .emailLogin: return EmailLoginViewController.make(withDependency: ()) case .atteLogin: switch event { case .loginWithAtte(.some(let resource), .some(let token)): return AtteLoginViewController.make(withDependency: .init(resource: resource, token: token)) default: return nil } case .home: switch event { case .loginAndShowHome(.some(let token), let userId): return HomeViewController.make(withDependency: .init(token: token, userId: userId)) default: return nil } default: return nil } ڊେ if elseͱେͯ͠มΘΜͳ͍…
32. ભҠάϥϑΛఆٛ let registrationGraph: [Transition<RegistrationState, RegistrationEvent>] = [ Transition(from: .root, to:

.registerRoot, by: DefaultEvent.showRegisterRoot), Transition(from: .registerRoot, to: .profileRegister, by: DefaultEvent.registerWithFacebook), Transition(from: .registerRoot, to: .startWithMercari, by: DefaultEvent.startWithMercari), Transition(from: .registerRoot, to: .emailLogin, by: DefaultEvent.loginWithEmail), Transition(from: .registerRoot, to: .atteLogin, by: DefaultEvent.loginWithAtte), Transition(from: .registerRoot, to: .home, by: DefaultEvent.loginAndShowHome), ] let registrationMachine = Automaton<RegistrationState, RegistrationEvent>( initialState: .root, transitions: registrationGraph ) DefaultEvent !?
33. DefaultEvent ͬͯԿʁ • Swift 3 ͷassociated value enumʹ͸σϑΥϧτ஋ઃఆͰ͖ͳ͍ • Acceptedͷεςʔλε͕ͩSwift4ʹೖ͍ͬͯͳ͍

Proposal: SE-0155 struct DefaultEvent { static let showRegisterRoot: RegistrationEvent = .showRegisterRoot() static let registerWithFacebook: RegistrationEvent = .registerWithFacebook(profile: nil) static let startWithMercari: RegistrationEvent = .startWithMercari(resource: nil, token: nil) static let loginWithEmail: RegistrationEvent = .loginWithEmail() static let loginWithAtte: RegistrationEvent = .loginWithAtte(resource: nil, token: nil) static let loginAndShowHome: RegistrationEvent = .loginAndShowHome(token: nil, userId: 0) }

36. ભҠؔ਺ (aka Reducer) • ༗ݶΦʔτϚτϯ • ભҠؔ਺ͷΈ (s, a) ->

s • Mealy Machine • ભҠؔ਺ (s, a) -> s • ग़ྗؔ਺ (s, a) -> b • Moore Machine • ભҠؔ਺ (s, a) -> s • ग़ྗؔ਺ s -> b
37. ભҠؔ਺ (s, a) -> s ग़ྗؔ਺ (s, a) -> b

Mealy Machine / Moore Machine ભҠؔ਺ (s, a) -> (s, b) ભҠؔ਺ (s, a) -> s ग़ྗؔ਺ s -> b ભҠؔ਺ s -> (a -> s, b)
38. ભҠؔ਺ (s, a) -> s ग़ྗؔ਺ (s, a) -> b

Mealy Machine vs Elm ભҠؔ਺ (s, a) -> (s, b) Elm Architecture fun update(state: State, action: Action): Pair<State, Command>
39. ભҠؔ਺ (s, a) -> s ग़ྗؔ਺ (s, a) -> b

Mealy Machine ભҠؔ਺ (s, a) -> (s, b) ભҠؔ਺ a -> s -> (s, b)
40. ભҠؔ਺ (s, a) -> s ग़ྗؔ਺ (s, a) -> b

Mealy Machine ભҠؔ਺ (s, a) -> (s, b) ભҠؔ਺ a -> s -> (s, b) Stateful Computation !!
41. ঢ়ଶ෇͖ͷܭࢉʢStateful Computationʣ ঢ়ଶ෇͖ͷܭࢉͱ͸ɺ͋Δঢ়ଶΛऔͬͯɺߋ৽͞Εͨঢ়ଶͱҰॹʹܭࢉ݁ՌΛ ฦؔ͢਺ͱͯ͠දݱͰ͖Δɿ s -> (s, a) class State<S,

A> { private let run: (S) -> (S, A) init(f: @escaping (S) -> (S, A)) { self.run = f } func run(s: S) -> (S, A) { return self.run(s) } }
42. εςʔτϞφυ ঢ়ଶ෇͖ͷܭࢉͱ͸ɺ͋Δঢ়ଶΛऔͬͯɺߋ৽͞Εͨঢ়ଶͱҰॹʹܭࢉ݁ՌΛ ฦؔ͢਺ͱͯ͠දݱͰ͖Δɿ s -> (s, a) extension State {

func map<B>(g: @escaping (A) -> B) -> State<S, B> { return State<S, B> { s in let (s1, val) = self.run(s: s) return (s1, g(val)) } } func flatMap<B>(g: @escaping (A) -> State<S, B>) -> State<S, B> { return State<S, B> { s in let (s1, val) = self.run(s: s) return g(val).run(s: s1) } } }
43. Ϟφυ = ﬂatmappable ܕ Image credit: Functors, Applicatives, And Monads

In Pictures

State Monad I/O Monad … Swift Optional antitypical/Result Statically typed Dependency Injection Signal / Observable Mealy Machine
45. ભҠؔ਺ (s, a) -> s ग़ྗؔ਺ (s, a) -> b

Mealy Machine ભҠؔ਺ (s, a) -> (s, b) ભҠؔ਺ a -> s -> (s, b) ભҠؔ਺ a -> State[s, b] εςʔτϞφυ

-> State<S, B> private var f : T init(f: @escaping T) { self.f = f } func transition(from: S, by: A) -> (S, B) { return f(by).run(s: from) } } ɹભҠؔ਺ A -> State[S, B] A:ΠϯϓοτܕɺB: Ξ΢τϓοτܕ A = ΠϕϯτenumɺB = UIViewController