次に本実験で用いる DTRAM の概要について説明します.
現時点では本ページの内容を完全に理解する必要はありません.課題に取り組む際に必要に応じて本ページを参照しながら進めるようにください.
DTRAM は仕様とアーキテクチャの両方を同時に記述することが可能な言語で,形式的仕様記述言語であると同時に形式的アーキテクチャ記述言語でもあります.
形式的アーキテクチャ記述言語とは,システム全体の構造を箱や線などの図ではなく,厳密に意味が定義された言語の形式で記述する方法です.形式的アーキテクチャ記述言語では一般に,
DTRAM は仕様とアーキテクチャの両方を形式的に記述することが可能な言語です.形式的仕様記述言語として見ると表現能力がかなり高いため,網羅的な解析はできません.
DTRAM では仕様そのものの解析より,
DTRAM では,リソースとチャンネルを使って仕様とアーキテクチャを記述していきます.
DTRAM におけるリソースとは,システムの外部から見えるデータのことです.Web サービスにおいて URL でアクセスされるリソースとほぼ同じものと考えて差し支えありません.DTRAM においてもリソースは階層構造を持つことができます.
チャンネルはリソースとリソースの間の通信路です.システム外部からの入力はイベントチャンネルと呼ばれる特殊なチャンネルによって受け付けられます.
リソースはチャンネルからメッセージを受け取ると状態遷移を行います.そのため,リソースは状態を持ちます.宣言型の記述とは違って,状態が変わるたびに別のリソースが生成されることもありません.すなわち DTRAM では,時間経過を扱うことができます.
それでは,具体的に DTRAM の記述を見ていきましょう.
Alloy のときに記述したアドレス帳と同じ仕様を,今度は DTRAM を用いて記述することを考えます. アドレス帳の仕様を再掲します.
DTRAM では,チャンネル単位で仕様を記述していきます.たとえばリソースに対する操作毎に,イベントチャンネルを定義していきます.ここでは,初期化操作用のイベントチャンネルを Init
,add 操作用のイベントチャンネルを Add
,del 操作用のイベントチャンネルを Del
とします.
DTRAM には,メインメソッドやメインスレッドに相当するものはなく,いずれかのイベントチャンネルが外部からのメッセージを受信するたびに状態遷移を行い,状態遷移後は次のメッセージが外部から入力されるまで何もせず待機します.リソースについては次に説明します.
DTRAM では,Web サービスと同様に階層化されたリソースを扱うことができます.リソースは状態を持ち,その状態を変更することができます.アドレス帳の仕様は次のような階層化されたリソースで表すこととします.
book
: アドレス帳全体book.owner
: アドレス帳の所有者book.addr
: アドレス帳に記載された連絡先の名前と住所の対応表book.addr.{name}
: 連絡先の名前 name
に対応した住所Init
を流れるメッセージを受け取るリソースは book.owner
です.同様に,チャンネル Add
内のメッセージを受け取るリソースは book.addr
,チャンネル Del
内のメッセージを受け取るリソースも book.addr
になります.
メッセージを受け取ったリソースは状態遷移を行います.各リソースの状態遷移は状態遷移関数によって定義されます.
たとえば,Add
チャンネルでメッセージを受け取った book.addr
リソースの状態遷移は,状態遷移関数
book.addr(pre_addr:によって定義されています.Map , add(name:Str , addr:Str )) = insert(pre_addr, name, addr)
book.addr
が関数名に相当します.状態遷移関数の関数名にはリソース名をそのまま用います.book.addr
は,2つの引数を受け取っています.DTRAMでは,状態遷移関数の第1引数は遷移前の状態,第2引数は受け取るメッセージ,式の右辺は遷移後の状態を表しています.add(name, addr)
を受け取った book.addr
リソースが,状態を pre_addr
から insert(pre_addr, name, addr)
に変化させることが示されています.insert()
関数は,第1引数に渡された写像(対応表)に対し,第2引数と第3引数の対応を追加した結果得られる写像を返す関数です.
したがって,上の式の右辺は,遷移前の状態 prev_addr
に対して,メッセージ add()
の引数で渡された name
と addr
の対応を追加した写像が遷移後の状態になることを示しています.:
の直後に変数の型を記述することができます.型推論によって型がわかる場合には,型を省略することもできます.状態の型が,List
,Map
,Json
であるリソースは子リソースを持つことができます.
Alloy と異なり,DTRAM では仮想実行に関する情報を仕様記述には記載しません.
channel Init {out book.owner(pre_name:Str , init(name:Str )) = name }channel Add {out book.addr(pre_addr:Map , add(name:Str , addr:Str )) = insert(pre_addr, name, addr) }channel Del {out book.addr(pre_addr, del(name:Str )) = delete(pre_addr, name) }
DTRAM でも実行を可視化することができます.Alloy とは異なり,DTRAM ではツールを用いてインタラクティブに仮想実行を進めていきます.まず,システムの初期状態を可視化したものが以下の図です,
可視化では1つのリソースが,1つの楕円で表されます.子リソースは親リソースの内側の領域に表示されます.リソースの上部にはリソース名が記され,リソースの中心にはリソースの現在の状態が記されています.
DTRAM の仮想実行では,メッセージを送りたいリソースをダブルクリックします.まず,book.owner
リソースに,Init
チャンネルを通じて,init("Suzuki")
というメッセージを送ることを考えます.
book.owner
リソースをダブルクリックします.
リソースに送信するメッセージの種類を選択する画面が表示されるので,init を選択します.
次にリソースに送信するメッセージの内容を入力します.ここでは,init("Suzuki")
を入力します.
次の状態に遷移します.book.owner
リソースの状態が,"Suzuki"
に変わったことが分かります.
同様にして,book.addr
リソースをダブルクリックして,Add
チャンネルを通じて,add("Satou", "Tokyo")
というメッセージを送ります.
book.addr
リソースの子リソースとして,book.addr.Satou
リソースが追加されたことが分かります.
また,book.addr.Satou
リソースの状態が,"Tokyo"
になっていることも分かります.
さらに,book.addr
リソースをダブルクリックして,add("Tanaka", "Osaka")
というメッセージを送ります.
book.addr
リソースの子リソースとして,さらに book.addr.Tanaka
リソースが追加され,その状態は "Osaka"
になっています.
次に,book.addr
リソースをダブルクリックして, del("Satou")
というメッセージを送ります.
del を選択して, del("Satou")
を入力します.
book.addr.Satou
リソースが削除されました.
DTRAM ではこのようにして実行が可視化されます.