関数としての関数


機能的反応プログラミングは、時間tにおける以前の値に基づいて時間tにおける電流値を計算する必要がある.しかし、その前の値が機能であるならば、どうですか?それはどのように見えるかを見るために時間をアンロールしましょう.私たちの機能はFizzBang to Number .
data FizzBang = Fizz | Bang

initialValue v = case v of
  Fizz -> 1.0
  Bang -> 0.0
valueAt1 v = initialValue v + case v of
  Fizz -> 3.0
  Bang -> 1.0
valueAt2 v = valueAt1 v + 1.0
valueAt3 v = valueAt2 v + case v of
  Fizz -> 0.0
  Bang -> 3.0
currentValue v = valueAt3 v + 6.3
時間が経過すると、関数呼び出しスタックはかなり深くなります.currentValue は4つの関数を呼び出す必要があります.valueAt3 , valueAt2 , valueAt1 and initialValue 現在の値を取得するには
私たちが以前のタイムスタンプから値を格納することができたならば、私たちが常にそれらを再計算する必要がないように、それはよりよいでしょう.しかし、どのように我々はこれを行うことができますか?つの解決策は、関数の初期評価を強制するクロージャを使用することです.
data FizzBang = Fizz | Bang

memoize :: (FizzBang -> Number) -> (FizzBang -> Number) -> FizzBang -> Number
memoize f g = let
    fizz = f Fizz
    bang = f Bang
  in
    \v -> case v of
      Fizz -> fizz + g Fizz
      Bang -> bang + g Bang

initialValue v = case v of
  Fizz -> 1.0
  Bang -> 0.0
valueAt1 = memoize initialValue (case _ of
  Fizz -> 3.0
  Bang -> 1.0)
valueAt2 = memoize valueAt1 (const 1.0)
valueAt3 = memoize valueAt2 (case _ of
  Fizz -> 0.0
  Bang -> 3.0)
currentValue = memoize valueAt3 (const 6.3)
これは関数呼び出し問題を解決しますが、それは非常に拡張できません.我々は機能を覚えるたびに、我々はそれらのすべてを作成する必要がありますlet ステートメントと追加のクロージャ.
私が最近プピュラーで使用した1つの戦略は、機能から記録への自然な転換を使用している抽象的な回帰すことです.
data FizzBang = Fizz | Bang

newtype FizzBang' a
  = FizzBang'
  { fizz :: a
  , bang :: a
  }

class Memoizable f g | f -> g, g -> f where
  memoize :: Function f ~> g
  functionize :: g ~> Function f

instance memoizableFizzBang :: Memoizable FizzBang FizzBang' where
  memoize f =
    FizzBang'
      { fizz: f Fizz
      , bang: f Bang
      }
  functionize (FizzBang' { fizz }) Fizz = fizz
  functionize (FizzBang' { bang }) Bang = bang

eval = functionize <<< memoize

initialValue v = case v of
  Fizz -> 1.0
  Bang -> 0.0
initialValue' = eval initialValue
valueAt1 v = initialValue' v + (case v of
  Fizz -> 3.0
  Bang -> 1.0)
valueAt1' = eval valueAt1
valueAt2 v = valueAt1' v + 1.0
valueAt2' = eval valueAt2
valueAt3 v = valueAt2' v + (case v of
  Fizz -> 0.0
  Bang -> 3.0)
valueAt3' = eval valueAt3
currentValue v = valueAt3' v + 6.3
これは、型クラスの使用を通して拡張可能なままである間、何かを本来の機能的な構文に近づけることの利点を持ちます.他の人も同様に役に立つと思います!