残念ながら、「イベント」と「動作」を 1 つのエンティティ「シグナル」にまとめることはあまりうまくいきません。
私が知っているほとんどのシグナルベースの FRP 実装は、次の行に沿って追加の「イベント」のようなタイプを作成することになります
type Event a = Signal (Maybe a)
したがって、イベントの概念はなくなるわけではなく、実際の単純化もありません。実際、シグナルタイプは意味論的な複雑化であると私は主張します。シグナルは、実装が簡単であるという理由だけで人気があります。
信号に対する主な議論は、離散イベントに対応する必要があるため、連続時間の動作を表すことができないということです。コナル・エリオットの最初のビジョンでは、行動は時間の単純な連続関数でした
type Behavior a = Time -> a
-- = function that takes the current time as parameter and returns
-- the corresponding value of type a
対照的に、信号は常に離散化され、通常は固定のタイム ステップに関連付けられます。(可変時間ステップ信号の上にイベントと動作の両方を実装することは可能ですが、それ自体は適切な抽象化ではありません。) これをイベント ストリームと比較してください。
type Event a = [(Time,a)]
-- list of pairs of the form (current time, corresponding event value)
個々のイベントが必ずしも一定間隔で発生するとは限りません。
ビヘイビアとイベントの違いについての議論は、それらの API がまったく異なるということです。主なポイントは、製品タイプが異なることです。
(Behavior a , Behavior b) = Behavior (a,b)
(Event a , Event b ) = Event (a :+: b)
つまり、動作のペアはペアの動作と同じですが、イベントのペアはコンポーネント/チャネルのいずれかからのイベントと同じです。もう1つのポイントは、2つの操作があることです
(<*>) :: Behavior (a -> b) -> Behavior a -> Behavior b
apply :: Behavior (a -> b) -> Event a -> Event b
ほぼ同じ型ですが、セマンティクスがまったく異なります。(最初の引数は、最初の引数が変更されたときに結果を更新しますが、2 番目の引数は変更しません。)
要約すると、シグナルは FRP の実装に使用でき、新しい実装手法を試すのに役立ちますが、FRP を使用したいだけの人にとっては、動作とイベントの方が抽象化に適しています。
(完全開示: 私はHaskell で react -bananaと呼ばれる FRP ライブラリを実装しました。)