遷移を実行する場所を一箇所に閉じ込める
peithoのスライド送りには入力源がいくつもある。キーボード、発表者画面のボタン、スマホのリモコン、クリック。素朴に作れば、それぞれが「次のスライドへ移る」処理を持つ。
peithoは不変条件を一つ置いてこれを避けている。
Only the shell ever executes transitions.
—peitho/docs/PEITHO_KICKOFF.md §16
UIの部品は peitho:navigate {to:"next"} という要求を投げるだけで、自分では動かない。実際の遷移はシェルだけが実行し、結果を peitho:slidechange として全員に通知する。入力源が増えても、状態が変わる場所は一箇所のままである。
要求(UI → シェル)
peitho:navigate / peitho:timercontrol。投げるだけで、自分では状態を変えない。
通知(シェル → 全員)
peitho:slidechange / peitho:presentationstart。実行した結果を配る。
{to: "next"} という書き方も効いている。「3番へ行け」ではなく「次へ」という意図を送るので、次がどこかを決めるのはシェルだけになる。飛ばすスライド(skip)や段階表示(reveal)が後から入ったとき、増えた解決規則はシェルの中だけで済んだ。
依存の向きが一方向であること
もう一つの規則が対になっている。
The slide body may listen to events, but may not issue requests.
—peitho/docs/PEITHO_KICKOFF.md §16
スライド本体はイベントを聞いてよいが、要求を出してはいけない。理由が具体的で良い — 配布用の成果物にはシェルが入っていない。peitho build が出すのはスライド本体だけで、プレゼン実行時のシェルは peitho present が一時領域に生成する。スライド本体がシェルの存在を前提にしていたら、配布物で壊れる。
つまりこの一方向性は行儀の良さではなく、同じ本体を二つの入り口(配布用と発表用)から使い回すための必要条件である。共有されている実体は一つで、入り口が二つ。本体が入り口を知っていたら共有できない。
「シェルを含めるか否か」はemit時の注入の切り替えにすぎず、再ビルドを要さない。これは構成データ出力DSLから貰えるのは評価段階だけであると同じ発想 — 中間表現を一度作り、出力段でだけ分岐させる。入力側で分岐しないから、真実の源が割れない。
抽象の境界として
UI部品はシェルの内部状態を知らず、シェルはUI部品が何個あるかを知らない。両者を繋いでいるのはイベントの語彙だけである。抽象化境界の透明性は記述自由度とは独立した軸であるの言う不透明さが、ここでは要求と通知の非対称として実現されている — 下から上へは要求しか通らず、上から下へは通知しか通らない。
窓をまたぐ同期はさらに一段外側にあり、シェルが橋渡しする(まとめられる通り道にトグルを流してはいけない)。UI部品は窓の中のイベントだけ知っていればよく、複数窓の存在を知らない。層を足すたびに全部品が知るべきことが増える、という設計を避けている。