• SwiftUI + Combine support (HarvestStore) • Works as a Dependency Container • Effect cancellation support • Effect Queue Management using FRP • Optics (Lens & Prism): Modularization of State, Action, Reducer, and their compositions
time • Stream of Stream ( Publisher<Publisher<T>>) is same as effects being enqueued by Effect Queue • Queued effects can be flattened into a single stream • FlattenStrategy for Effect Queue • merge, concat, concurrent(max:), switchLatest, race, etc (derived from ReactiveSwift)
to build a dataflow pipeline with hiding the internal states of Publisher • But more hiding will cause harder state management • Use Elm Architecture together with FRP to ignore trivial state management • Examples: Rx.throttle to memorize previous time, or Effect Queue (State) Management
(struct product type) • Example: Get user name & set user name • Prism: 2 operations for Action (enum sum type) • Example: Build command & get command's option value struct User { enum Command { var name: String case rm(rf: Bool) } }
{ ... } let childReducer1: Reducer<ChildAction1, ChildState1> = ... let childReducer2: Reducer<ChildAction2, ChildState2> = ... // Converts child 1 & 2's reducer types based on parent types // and combine with the same parent types. let reducer: Reducer<Action, State> = combine([ contramapS(lens1)(contramapA(prism1)(childReducer1)) contramapS(lens2)(contramapA(prism2)(childReducer2)) ])
(struct) and Action (enum) modular per UI component, and keeps manageable by forming a tree structure • Lens and Prism becomes important when combining each UI component's Reducers into one AppReducer (better version of react-redux) • Beauty of Optics can be found in Functional Programming and Category Theory
Multi-Store Architecture: Child components communicate with parent components and reactively synchronizes states • swift-case-paths as Smart Prism • WritableKeyPath (Swift standard type) as Lens • Over ˑ2000, good documentation & video tutorials
(47 Degrees) • Bow: Functional programming library using "Lightweight Higher Kinded Polymorphism" technique • Can write e.g. func foo<M: Monad> • cf. inamiy/HigherKindSwift (experimental) • Comonadic UI: UI Architecture on top of Category Theory (Mathematics)
• Comonad: Holds internal state and calculate output • Ex: Iterator pattern outputs next value from internal state • Ex: Builder pattern accumulates state and output • Ex: React Component (SwiftUI) owns state and render (body) VirtualDOM (View)
state: S // Calculates virtual view from current state. let _body: (S) -> V // Note: Actual type signature is `Self -> V` var body: V { _body(state) } } Let Component<S, V> = W<V>, then we get: body: W<V> -> V ɾɾɾ Comonad's extract property
func extract<D>(_ wd: W<D>) -> D static func duplicate<D>(_ wd: W<D>) -> W<W<D>> } • extract: From object W<D> to consume context to output Dʢe.g. Use state to output viewʣ • duplicate: Generates all the possible futures of object
-> Component = Component(_body: ...) // Note: Partial apply let component: Component = makeComp(state: ...) // Current Component // Component that generates all possible futures of Component duplicate(component) = Component(_body: makeComp, state: component.state) ≈ [ /* Duplicates current component */, /* Duplicate + some state changes */, /* Duplicate + another possible state changes */, ... ] // Forms a "space" of all possible states for Component
Current state var state: S /// Calculates virtual view from current state let _body: (S) -> V } struct Store<S, A> { let state: S let render: (S) -> A }
/// Current state var state: S /// Calculates virtual view /// from current state AND event handler let view: (S) -> (EventHandler) -> V } typealias EventHandler = (Action) -> IO<Void> /* Effect */
(n = 0, 1, 2, l) is the state- mutating (can be expressed as monadic query) // Infinite stream (Comonad) // Shift query (Monad) indirect enum Stream<A> { indirect enum Shift<A> { case cons(A, Stream<A>) case done(A) } case shift(Shift<A>) } Q. What is the relationship between these two?
G] { /// Natural transformation from Day convolution to Identity /// i.e. `Day f g ~> Identity` static func pair<A, B, C>(f: (A, B) -> C) -> F<A> -> G<B> -> C } extension Pairing[Shift, Stream] { static func pair<A, B, C>(f: (A, B) -> C) -> Shift<A> -> Stream<B> -> C { switch (shift, stream) { case let (.done(a), .cons(b, _)): return f(a, b) case let (.shift(nextShift), .cons(_, nextStream)): return pair(f)(nextShift)(nextStream) } } }
as monad query (explained later). struct Store<S, A> { let state: S let render: (S) -> A } struct State<S, A> { let runState: S -> (A, S) } extension Pairing[State, Store] { ... }
struct Co<W, A> { let runCo<R>: W<A -> R> -> R } // `Co<W>` will be the derived monad for any comonad `W`. extension Monad[Co<W>] where Comonad[W] { static func `return`<C>(_ c: C) -> Co<W, C> { Co { wf in W.extract(wf)(c) } } static func join<C>(_ mmc: Co<W, Co<W, C>>) -> Co<W, C> Co { (wc2r: W<C -> R>) in mmc.runCo( W.extend({ wc2r in { (mc: Co<W, C>) in mc.runCo(wc2r) } })(wc2r) ) } } }
Co<W, A> ≅ ∀R. W<A -> R> -> R // Definition Co<Store<S>, A> ≅ ∀R. Store<S, A -> R> -> R ≅ ∀R. (S, (S -> A -> R)) -> R ≅ S -> ∀R. (S -> A -> R) -> R // currying ≅ S -> ∀R. ((S, A) -> R) -> R // uncurrying ≅ S -> (S, A) // Yoneda Lemma: ∀R. (X -> R) -> R ≅ X ≅ State<S, A>
state • Comonad duplicate makes possible futures of comonad • struct Co<W, A> creates state-updating monad query from comonad W • func select picks one of the future of comonad by using pairing monad query
an "object" in OOP, separating the concerns into "comonad" and "side-effect" • If W = Store comonad, State monad querying with side- effects means calling React's setState • Equivalent to SwiftUI's @State mutation • Note: Components can be nested using comonad transformer
Input, A = Output // Current output and "input to future comonad" case runMoore(A, I -> Moore<I, A>) } Same type as ∃S. (initial: S, reducer: S -> I -> S, render: S -> A), also known as Moore State Machine. Example: Elm Architecture, Redux
functor `F`. // `F(X) = I -> X` will be Moore, `F(X) = ()` will be `∃S.Store<S>` indirect enum Cofree<F, A> { case runCofree(A, F<Cofree<F, A>>) } Free Monad (Free<F, A>) will be the pairing query DSL. Example: PureScript Halogen
structure defines UI architecture patterns • SwiftUI, React, Elm, PureScript Halogen, etc... • Optics for modularizing states, actions, reducers, and combine them all in an elegant way • Functional Programming (and Category Theory): A tool for understanding the essence of programming
ˑ 100 Author's activity ❓ Difficulty Medium Medium Hard Effects Combine Combine BowEffects Optics FunOptics WritableKeyPath & CasePaths BowOptics Comonadic UI Moore Moore Any Comonads Inspired from Elm Elm & React PureScript & Category Theory
State Machine, inspired by Elm • Composable Architecture • bow-arch: Comonadic UIs • Brandon Williams - Lenses in Swift • Lenses and Prisms in Swift: a pragmatic approach | Fun iOS
And the Future is Comonadic! • The Future Is Comonadic! - Speaker Deck • Comonads for user interfaces - Arthur Xavier • A Real-World Application with a Comonadic User Interface • The Comonad.Reader » Monads from Comonads
• ! Reactive State Machine - iOS Conf SG 2016 - YouTube • " Make Elm Architecture in Swift • ! React & Elm inspired frameworks in Swift • " Higher Kinded Types in Swift • " Category Theory in Swift / iOSDC Japan 2018 • " Category Theory and Programming