TLA+入門1: TickTack
概要
このページでは最初の TLA+ の例として、システム状態に 0 と 1 を交互にとる TickTack システムについて考える。
Table of Contents
TickTack の抽象化
TLA+ による仕様の構築は経験的に以下のような手順で行うと良い。
- 目的のシステムに必要な振る舞いを定義する。
- 振る舞いが機能するために必要な状態変数を定義する。
- 全ての振る舞いを初期状態 (Init) と次の状態 (Next) で記述する。
- 振る舞いが正しいことをチェックするアサーションを作成する。
まず最初に、この TickTack システムの要件は以下の一つの振る舞いで表すことができる。
システムは 0 と 1 の状態を交互にとる。
この振る舞いを表すためには 0 か 1 かどちらかの値をとる単一の状態変数があれば十分であろう。この状態変数を \(t\) とし、その初期状態は 0 または 1 のどちらでも許容できるため式 (\(\ref{init}\)) のように定義することができる。\[ \begin{equation} {\it Init} \ldef t \in \{0, 1\} \label{init} \end{equation} \]
TLA+ ではあるステップの状態変数を \(x\) としたとき、その変数の次のステップでの状態を \(x'\) (\(x\)-プライム) と表現する。言い換えると「\(x'\) はこのステップで \(x\) に起きた変化」である。つまりこの TickTack システムの例では、\(t=0\) は次のステップで \(t=1\) となることから \(t'=1\) であり、同様に \(t=1\) は次のステップで \(t=1\) となることから \(t'=0\) である。これは: \[ \begin{eqnarray} {\it Next} & \ldef & {\sf IF} \ t=0 \ {\sf THEN} \ t'=1 \ {\sf ELSE \ IF} \ t=1 \ {\sf THEN} \ t'=0 \nonumber \\ & \equiv & (t = 0 \land t' = 1) \lor (t = 1 \land t' = 0) \label{next} \end{eqnarray} \] と表すことができる。
さて、ここまで定義できればこの TickTack システムを UML の状態遷移図で表すことも容易であろう。なお、この TickTack システムは宇宙における時間という概念が有効な限り停止することはない。
基本的な TLA+ の記述
式 (\(\ref{init}\)) と (\(\ref{next}\)) より TickTack の仕様として必要なアサーションは以下のように表現できる。\[ \begin{eqnarray*} {\it Init} & \ldef & t \in \{0, 1\} \\ {\it Next} & \ldef & (t = 0 \land t' = 1) \lor (t = 1 \land t' = 0) \end{eqnarray*} \] TLA+ では \(t\) がどのような過程を経て \(t'\) に変化するかを詳細に説明することなく、ステップの前後の状態にのみ着目している。実際のプログラムで if 文を使うかビット反転命令を使うかは実装依存である。
この仕様を TLA+ で表現すると以下のような記述となる。順番に説明しよう。
TLA+ にはモジュールという大きな単位があり、1 行目と 7 行目のように ---- MODULE モジュール名 ----
で始まり ====
で終了する。またモジュール中の ----
は可読性のための罫線として使用することができる。これらは 4 文字以上連続する -
または =
であれば良い。
2 行目の VARIABLES
は変数宣言でシステムで使用する状態変数を定義している (最後の S
は省略可能で VARIABLE
でも良い)。ここでは 0 または 1 のシステム状態を保持するために一つの変数 \(t\) を宣言している。
3 行目の Init
はシステムの取りうる初期状態 \(t \in \{0,1\}\) を定義している。一般的なプログラミング言語が具体値を「代入」するのに対して、仕様記述言語では仕様が満たすべき条件を「定義」する点で少し意味が異なることに注意。
4-6 行目はシステムが次に取りうる状態を表す Next
と、その部品となる tick
および tack
を定義している。TLA+ では /\
が論理積 \(\land\)、\/
が論理和 \(\lor\) を表している。
t'
は次のステップで \(t\) がとるべき値である。したがって、例えば tick
は \(t=0 \land t'=1\) つまり「ステップ開始時の \(t\) が 0 で次のステップに引き継ぐ \(t\) が 1 となる」というアサーションを定義している。これは条件文として if \(t=0\) then \(t'=1\) と等価とも言える。
システムの "取りうる初期状態" と "次に取りうる状態" の定義がモデルファイル TickTack.cfg
の INIT
と NEXT
で指定できる。したがって Init
と Next
には任意の定義名をつけることができる。
またこの例では使用していないが TLA+ の記述では (* block comment *)
と \* line comment
形式のコメントを使用することができる。
TLC 検証結果
Fig 2 に示す仕様記述を検証すると (実行方法は TLC 検証参照) Fig 3 のような結果となり状態モデルに矛盾がないこと、つまり式 (\(\ref{init}\)) と (\(\ref{next}\)) に示す 2 つのアサーションが TickTack システムで取りうる全ての状態に対して成功することを確認することができる。
以上、TLA+ により TickTack システムの仕様は以下の 2 点に明文化されるステートマシンとして表現できることが確認できた。
- システムの初期状態は \(t \in \{0, 1\}\) である。
- 全てのステップで状態は \((t=0 \land t'=1) \lor (t=1 \land t'=0)\) である。
ここでカンのいい人は仕様に停止条件がないのになぜ TLC 検証が停止したかが気になったかもしれない。その話題については TLC 検査の特徴を参照。
エラーの検出
ではここで未定義の状態を生成するように修正を加えて TLC がどのように振る舞うかを見てみよう。Fig 4 は \(t=1\) の時に \(t'=2\) となる可能性を追加した仕様とその検証結果である。
このエラートレースは「初期状態 \(t=1\) で開始し tack
を通過したあとに \(t=2\) (未定義の状態) となり halt した」ということを伝えている。つまり設計者は \(t=2\) という予期しない状態を設計として排除するか、\(t=2\) となるケースを正しい仕様として考慮しなければならない。