1.基本的な信号関数
恒等関数:ある信号aをそれ自身(信号)aへ写像する信号関数である。
identity :: SF a a
定数:ある信号bを入力し、それを出力とする信号関数を出力する。
constant :: b -> SF a b
iPre:ある信号aを入力し、それを入出力とする信号関数を出力する。
iPre :: a -> SF a a
積分:ある信号(ベクトル空間)aと別の信号sに対して、aを入出力とする信号関数である。これは、信号aを入力し、それを積分した信号aを出力する信号関数を与える。
integral :: VectorSpace a s => SF a a
これは、数式で表すと\(y(t) = \int_0^t x ( t ) dt\)である。これは、\(x (t )\)がSF a aの最初のaで、\(y ( t)\)が、SF a aの最後のaである。また、sは\(t\)である。
なお、上記の基本的な信号関数はすべて「状態を有しない」信号関数である。
2.信号関数の合成
信号関数は、信号関数同士組み合わせることができる。その中で、最も単純な合成は下図の直列合成である。
直列合成は次のように表現される。
(>>>) :: SF a b -> SF b c -> SF a c
合成では、信号関数が主であり、信号は二の次であることに注意してほしい。(これがYampaの特徴である。)
合成を用いると次のような便利なことがある。
時間は下図のように定義できる。
即ち、信号関数constant 1.0 と信号関数integralの直列合成で、Timeを表現できる。
time :: SF a Time time = constant 1.0 >>> integral
即ち、\(t = \int_0^t 1.0 dt\)である。
Yampaには大域的な時間はなく、全て、信号関数が始まった時からの局所的な時間しかない。
3.複雑な合成
下図のように信号関数が合成された例を考えることにする。
信号関数の合成を考えるとき、直列合成の他にいくつかの合成を必要とするが、それを助けるのが、アロー記法である。arrは下図のようになっている。
arr fはaを入力としbを出力とする関数からaを入力としbを出力とする信号関数を作り出す。
arr :: (a -> b) -> SF a b
firstは下図のようになっている。
firstは、最初の信号関数SF a bと信号cを一緒にした信号関数SF (a c) (b c)を作り出す。
first :: SF a b -> SF (a c) (b c)
loopは下図のようになっている。
loopは、信号がループしている信号関数を定義する。Firstとは逆の関係にある。
loop :: SF (a c) (b c) -> SF a b
3.アロー記法の応用
アロー記法を利用した例をいくつか示す。
恒等な信号関数identityは、恒等関数idを信号関数化することで得られる。
identity :: SF a a identity = arr id
定数の信号関数constantは定数のconstを信号化することで得られる。
constant :: b -> SF a a constant b = arr (const b)
bを受けてcを出力する関数fと、入力aと出力bで定義されている信号関数sfを入力と、信号関数SF a cを出力する合成を^>>と定義する。これはsfとarr fを直列合成したものと同じである。
^<< :: (b -> c) -> SF a b -> SF a c f ^<< sf = sf >>> arr f
これまでの合成を応用すると色々な合成を定義することができる。まず、並列な信号関数を表す***は
また、二つの信号関数への分離を表す&&&は
これらの型シグネチャは次のようになる。
(***) :: SF a b -> SF c d -> SF (a,c) (b,d) (&&&) :: SF a b -> SF a c -> SF a (b c)
4.システムをアロー記法で表現する
アロー記法を用いると、信号関数によって複雑に合成されているシステムを信号関数のネットワークとして表現することができる。例えば、先に示したシステムは、下図のように表すことができる。
これをHaskellで表すと次のようになる。
loop (arr (\ (x, y) -> ((x,y),x))) >>> (first f >>> (arr (\(x,y) -> (x,xy))) >>> (g ***h))))
となる。