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

純粋関数型 React Hooks

Avatar for Tomoki Ohno Tomoki Ohno
November 09, 2018

純粋関数型 React Hooks

Avatar for Tomoki Ohno

Tomoki Ohno

November 09, 2018
Tweet

Other Decks in Technology

Transcript

  1. Hooks import { useState } from 'react' function HogeComponent() {

    const [count, setCount] = useState(0) return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ) }
  2. Stay Pure React コミッターのDan ⽈く https://medium.com/@dan_abramov/making-sense- of-react-hooks-fdbde8803889 Hooks could be

    implemented in a pure way using algebraic effects (if JavaScript supported them). “ “
  3. import { useState } from 'react' function HogeComponent() { const

    [count, setCount] = useState(0) const [name, setName] = useState("Usagi") return ( <div> <p>You clicked {name} {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ) }
  4. import { useState } from 'react' function HogeComponent() { return

    state => { /* const [count, setCount] = useState(0) const [name, setName] = useState("Usagi") return ( <div> <p>You clicked {name} {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ) */ } }
  5. import { useState } from 'react' function HogeComponent() { return

    0, state => { const {count, setCount} = state /* const [name, setName] = useState("Usagi") return ( <div> <p>You clicked {name} {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ) */ } }
  6. import { useState } from 'react' function HogeComponent() { return

    0, state => { const {count, setCount} = state return "Usagi", state2 => { const {name, setName} = state2 return ( <div> <p>You clicked {name} {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ) } } }
  7. Scala のfor 便利 a.flatMap(x => { b.flatMap(y => { c.map(z

    => z + 10) }) }) for { x <- a y <- b z <- c } yield z + 10
  8. Scala のfor 便利 def HogeComponent() = { useState(0).flatMap((count, setCount) =>

    { useState("Usagi").map((name, setName) => { <div> <p>You clicked {name} {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> } } }
  9. Scala のfor 便利 def HogeComponent() = for { (count, setCount)

    <- useState(0) (name, setName) <- useState("Usagi") vdom = <div> <p>You clicked {name} {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> } yield vdom
  10. useState 実装してみた enum CUSH[S, R] { // ComputationUsingStateHook State Result

    case Continue(continueWith: R) case Finish(finishWith: R) case Pause( initialState: S, continuation: ((S, S => Unit)) => CUSH[S, R] ) def flatMap[R2](f: R => CUSH[S, R2]): CUSH[S, R2] = this match { case Continue(continueWith) => f(continueWith) case Finish(finishWith) => f(finishWith) case Pause(initialState, continuation) => Pause( initialState, x => continuation(x).flatMap(f) ) } def map[R2](f: R => R2): CUSH[S, R2] = this.flatMap(x => Finish(f(x))) }
  11. useState 実装してみた def HogeComponent() = for { (count, setCount) <-

    useState(1) vdom = s"<div>${count + 10}</div>" } yield vdom
  12. useState 実装してみた def useState[S] (initialState: S) : CUSH[S, (S, S

    => Unit)] = Pause(initialState, x => Continue(x)) def injectState[S, R] (paused: Pause[S, R], state: S, setState: S => Unit) : CUSH[S, R] = paused.continuation((state, setState)) scala> val a = HogeComponent() val a: CUSH[Int, String] = Pause(1,CUSH$$Lambda$5769/1858659298 scala> injectState(a, 123, x => ()) val res0: CUSH[Int, String] = Finish(<div>133</div>)
  13. useContext 実装してみた enum CUCH[C, R] { // ComputationUsingContextHook case Continue(continueWith:

    R) case Finish(finishWith: R) case Pause(continuation: C => CUCH[S, R]) def flatMap[R2](f: R => CUCH[C, R2]): CUCH[C, R2] = this match { case Continue(continueWith) => f(continueWith) case Finish(finishWith) => f(finishWith) case Pause(continuation) => Pause(x => continuation(x).flatMap(f)) } def map[R2](f: R => R2): CUCH[C, R2] = this.flatMap(x => Finish(f(x))) }
  14. Freer モナド enum Freer[F[_], R] { case Pure(a: R) case

    Impure[F[_], R, R0](fa: F[R0], k: R0 => Freer[F, R]) extends Freer[F, R] def flatMap[R2](f: R => Freer[F, R2]): Freer[F, R2] = this match { case Pure(a) => f(a) case Impure(fa, k) => Impure(fa, x => k(x).flatMap(f)) } def map[R2](f: R => R2): Freer[F, R2] = flatMap(x => Pure(f(x))) }
  15. useState in Freer case class UseState[S, R]( initialState: S, typeConv:

    ((S, S => Unit)) => R ) type CUSH[S, R] = Freer[[T] => UseState[S, T], R] def useState[S](initialState: S) : CUSH[S, (S, S => Unit)] = Impure(UseState(initialState, x => x), x => Pure(x)) def injectState[S, R] (freer: CUSH[S, R], state: S, setState: S => Unit) = freer match { case Freer.Pure(a) => throw new Error case Freer.Impure( UseState(initialState, meansNothing), k ) => k(typeConv((state, setState))) }
  16. 型が合わない def HogeComponent() = for { (count, setCount) <- useState(0)

    (name, setName) <- useContext(NameContext) vdom = <div> <p>You clicked {name} {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> } yield vdom
  17. 型が合わない useState(0).flatMap { x => useContext(NameContext).map { y => def

    flatMap(fa: F[A], f: A => F[B]): F[B] def map(fa: F[A], f: A => B): F[B]
  18. Eff モナド enum Eff[F[_], R] { case Pure(a: R) case

    Impure[F[_], R, R0](fa: F[R0], k: R0 => Eff[F, R]) extends Eff[F, R] def flatMap[R2](f: R => Eff[F, R2]): Eff[F, R2] = this match { case Pure(a) => f(a) case Impure(fa, k) => Impure(fa, x => k(x).flatMap(f)) } def map[R2](f: R => R2): Eff[F, R2] = flatMap(x => Pure(f(x))) }
  19. Freer モナド enum Freer[F[_], R] { case Pure(a: R) case

    Impure[F[_], R, R0](fa: F[R0], k: R0 => Freer[F, R]) extends Freer[F, R] def flatMap[R2](f: R => Freer[F, R2]): Freer[F, R2] = this match { case Pure(a) => f(a) case Impure(fa, k) => Impure(fa, x => k(x).flatMap(f)) } def map[R2](f: R => R2): Freer[F, R2] = flatMap(x => Pure(f(x))) }
  20. 型のUnion type Hoge = number | string Array[Int] | Future[Int]

    | List[Int] | Option[Int] 型コンストラクタのUnion (Array | Future | List | Option)[Int]
  21. 型コンストラクタのUnion enum Union[Head[_], Tail[_], R] { case Value(value: Head[R]) case

    NextType(effects: Tail[R]) } type :+:[Head[_], Tail[_]] = [R] => Union[Head, Tail, R] type F = Array :+: Future :+: List :+: Option :+: Nothing val array: F[Int] = Value(Array(12, 34, 56)) val list: F[Int] = NextType(NextType(Value(List(123, 456))))
  22. Member 型コンストラクタE が型コンストラクタのUnionU に 含まれている場合、 この型クラスのインスタンスが存在する trait Member[E[_], U[_]] implicit

    def baseCase[Head[_], Tail[_]] = new Member[Head, Head :+: Tail] implicit def recursiveCase[Needle[_], Head[_], Tail[_]] (implicit m: Member[Needle, Tail]) = new Member[Needle, Head :+: Tail] Array :+: Future :+: List :+: Option :+: Nothing
  23. E[A] からE を含むUnion へ型変 換 trait Member[E[_], U[_]] { def

    lift[R](value: E[R]): U[R] } implicit def baseCase[Head[_], Tail[_]] = new Member[Head, Head :+: Tail] { def lift[R](value: Head[R]): (Head :+: Tail)[R] = Value(value) } implicit def recursiveCase[Needle[_], Head[_], Tail[_]] (implicit m: Member[Needle, Tail]) = new Member[Needle, Head :+: Tail] { def lift[R](value: Needle[R]): (Head :+: Tail)[R] = NextType(m.lift(value)) }
  24. E[A] からE を含むUnion へ型変 換 type U = (Array :+:

    Future :+: List :+: Option :+: Nothing) val l: U[String] = lift(List("abab", "fee")) // -> NextType(NextType(Value(List(abab, fee))))
  25. useState in Eff case class UseState[S, R]( initialState: S, typeConv:

    ((S, S => Unit)) => R ) def useState[U[_], S](initialState: S) (implicit m: Member[[R] => UseState[S, R], U]) : Eff[U, (S, S => Unit)] = Impure( m.lift(UseState(initialState, x => x)), x => Pure(x) )
  26. useContext in Eff case class UseContext[C, R](typeConv: C => R)

    def useContext[U[_], C]() (implicit m: Member[[R] => UseContext[C, R], U]) : Eff[U, C] = Impure(m.lift(UseContext(x => x)), x => Pure(x))
  27. case class NameContext(name: String) type Effects = ([R] => UseState[Int,

    R]) :+: ([R] => UseContext[NameContext, R]) :+: Nothing def HogeComponent(): Eff[Effects, String] = for { (state, setState) <- useState(1) context <- useContext() vdom = s"<div>count of ${context.name} is now $state" } yield vdom
  28. def runContext[U[_], C, R] (eff: Eff[([R] => UseContext[C, R]) :+:

    U, R], value: C) : Eff[U, R] = eff match { case Pure(r) => Pure(r) case Impure(Value(UseContext(typeConv)), k) => runContext(k(typeConv(value)), value) case Impure(NextType(u), k) => Impure(u, c => runContext(k(c), value)) }
  29. class State[S](var value: Option[S] = None) def runState[U[_], S, R](

    eff: Eff[([R] => UseState[S, R]) :+: U, R], state: State[S] ): Eff[U, R] = eff match { case Pure(r) => Pure(r) case Impure( Value(UseState(initialState, typeConv)), k ) => runState( k(typeConv(( state.value.getOrElse(initialState), (newState: S) => state.value = Some(newState) ))), state ) case Impure(NextType(u), k) => Impure(u, s => runState(k(s), state)) }
  30. case class NameContext(name: String) type UseNameContext[R] = UseContext[NameContext, R] type

    UseIntState[R] = UseState[Int, R] type Effects = UseIntState :+: UseNameContext :+: Nothing def HogeComponent(): Eff[Effects, String] = for { context <- useContext[Effects, NameContext] (state, setState) <- useState(1) vdom = s"<div>count of ${context.name} is now $state</div>" } yield vdom val state: State[Int] = new State // Eff[UseIntState :+: UseNameContext :+: Nothing, String] val a = HogeComponent() // Eff[UseNameContext :+: Nothing, String] val b = runState(a, state) // Eff[Nothing, String] val c = runContext(b, NameContext("birds")) val vdom: String = run(c)
  31. Freer モナド 中断with 情報 再開with 情報 が可能な計算 再開を別スレッドでやったり、 再開部分を何度も呼び出してループを実現したり、 表現⼒は無限⼤

    Eff モナド 複数のFreer を同時に使う⽅法 そのFreer に処理できることはそのFreer にやらせて、 できないことは別のFreer にやらせる