言語ゲーム

とあるエンジニアが嘘ばかり書く日記

Twitter: @propella

SOE 15.2 章 Implementing FAL

The Haskell School of Expression の 15.2 章以降 FAL の実装を読みます。15.1 章では、特に仕組みに触れず例題を使って FAL の説明を試みていました。次に FAL の実装に触れる事によって、反応プログラミングの原理を理解出来る流れになっています。

最初に Behavior 型の設計です。シンプルな Behavior の設計としては、次のような物があります。

newtype Behavior1 a 
    = Behavior1 ([(UserAction, Time)] -> Time -> a)

要するに、振る舞い(= 移り変わる何か)とは、動作と時刻の組(Squeak でいう所の Event) の列と、時刻を受け取り、何か(先ほどの例では色や文字列)を返す関数、または表のような物と考える事が出来るという物です。果たしてこのような事が実装可能でしょうか? 一番不可能そうに見える部分は、イベントの列を全部持つという点です。そして、大量のイベント列をどう検索するかという問題もあります。だけど、問題を少し工夫すれば解き方が見えてきます。

問題を単純化して、次のように抽象化します。

  • 無限個の、ばらばらな数の入った数列 A がある(例えば素数のような)。
  • もう一つ同じようにばらばらな数列 B がある。
  • どちらも数は小さい順に並んでいる。
  • A と B 両方に含まれる数を取り出す。


双方の数列の順序がばらばらなら途方に暮れる話ですが、どちらも順序良く小さい数から大きい数に並んでいるというのが味噌です。そのおかげで、先頭から調べて行けば簡単に重なる数を取り出す事が出来ますし、不要であれば過去の数字を忘れてしまっても構いません。Behavior も同じ方針で設計する事が出来ます。つまり、振る舞いとは、片や操作と時刻の列があり、片や実時間の流れがあり、双方を比べながら適切な時に適切な値を生み出す装置と考えるのです。

そしてその装置同士を組み合わせて大きな装置を作って行く事が反応プログラミングです。反応プログラミングの「副作用が無い」とは、装置同士の組み合わせに副作用が無い事を言います。もちろん流れ行く世界には副作用を垂れ流すわけですが、装置自体は純粋関数的な存在なので、それを数学的に扱い、意味を変えないで機械的に操作し、単純化(簡約・リファクタリング)する事が出来ます。これにより今までとは格段に複雑なプログラムも、簡単に扱う事が出来るでしょう。

と、話が脱線しましたが、実装に戻ります。改良した版として、次の定義は如何でしょう?

newtype Behavior2 a 
    = Behavior2 ([(UserAction,Time)] -> [Time] -> [a])

第二引数と返り値がリストになりました。これだけで、上記の時間を辿りながら反応して行く装置を作れます。実はもう一つ、リストをストリームとして扱う方法(14章)を理解しなければこの定義は理解出来ないのですが省略します。

ここで気づくのが、Time が二つあるという点です。「コンピュータはイベントが起こらないと何もしない」と決める事によって一つ取り去る事が出来ます。

newtype Behavior3 a 
    = Behavior3 ([UserAction] -> [Time] -> [a])

現在時刻を表す time のように、イベントが起こらない事も考えて [UserAction] を [Maybe UserAction] にします。

newtype Behavior4 a 
    = Behavior4 ([Maybe UserAction] -> [Time] -> [a])

最後に、メモ化する(?)という観点からカリー化をやめてタプルを使います。

newtype Behavior a 
    = Behavior (([Maybe UserAction],[Time]) -> [a])

これが FAL の Behavior の定義です。さて、ここの後本では Event 型の設計に移りますが、読んでも良く分からなかったので飛ばします。型の設計の後に色々実装が書いてあります。ここはさくっと。

  • time : UserAction を無視して今の時刻をそのまま返すだけです。
  • constB : UserAction も時刻も無視して同じ値を返します。返すのはストリームなので、[1, 1, 1, 1...] のような値になります。
  • red, blue, ... : constB で定義します。
  • lift1, lift2, ... : 普通の値向けの関数を、Behavior に使えるようにします。

で、いよいよ untilB の定義に移るのですが、急に難しくなるので休憩します。ここから先は、14章ストリームを復習しないと意味が分からないです。面倒くさい。。。しかし、untilB による Behavior と Event の合成と、17 章 reactimate の実装によるアニメーションのレンダリングが分かれば悟りが開けると期待しています。