Upload
soleil
View
68
Download
10
Embed Size (px)
DESCRIPTION
OCommand : 型安全な シェルプログラミングのための 領域特化言語の提案. 朝倉 泉 増原 英彦 青谷 知幸 東京工業大学 数理・計算 科学専攻. OCommand. OCaml 上 で安全にシェルコマンドを 呼び出すための処理 系 コマンドの仕様を書くための DSL コマンドの結果にアクセスする書式を変換する処理 系 コマンド 出力 への アクセス に対する 安全性 を保証. ls コマンドを利用するプログラム例. ls -l の出力からサイズ の合計 が与えられた閾値 より 小さいファイル 集合を求めたい 例 閾値 =10000Byte. - PowerPoint PPT Presentation
Citation preview
OCommand : 型安全なシェルプログラミングのための
領域特化言語の提案
朝倉 泉増原 英彦青谷 知幸
東京工業大学 数理・計算科学専攻1
OCommand• OCaml 上で安全にシェルコマンドを
呼び出すための処理系–コマンドの仕様を書くための DSL–コマンドの結果にアクセスする書式を変換す
る処理系• コマンド出力へのアクセスに対する
安全性を保証
2
ls コマンドを利用するプログラム例
• ls -l の出力からサイズの合計が与えられた閾値より小さいファイル集合を求めたい
• 例 閾値 =10000Byte
3
$ ls –l-rw-r----- 1 root admin 2682 Dec 17 20:37 a-rw-r----- 1 root admin 2459 Jan 10 14:54 b-rw-r----- 1 root admin 2397 Jan 10 14:55 c-rw-r--r-- 1 root wheel 607 Jan 16 07:53 d-rw-r----- 1 root admin 12217 Feb 4 17:45 e-rw-r----- 1 root admin 12986 Feb 5 00:02 f
ファイルサイズ ファイル名
{perm:perm; hard:int; ... ;size:int; ... ;name:string}
各欄が適切なデータ型に変換されたレコード型として扱いたい
lsコマンドを利用するプログラム例• コマンドはオプション集合を引数に取る関数 ( コマン
ド関数 )• コマンド関数の戻り値はレコード ( 出力行レコード )
のリストlet collect_files size = let files = Ls.command (add_l empty) in let rec iter cur_size acc files = match files with | [] -> acc | file :: files -> let cur_size = cur_size + file..size in if cur_size > size then acc else iter cur_size (file..name :: acc) files in iter 0 [] files;;
4
指定されているオプションの集合
( オプション集合 )
目標 1/5. コマンドの関数化
let collect_files size = let files = Ls.command (add_l empty) in let rec iter cur_size acc files = match files with | [] -> acc | file :: files -> let cur_size = cur_size + file..size in if cur_size > size then acc else iter cur_size (file..name :: acc) files in iter 0 [] files;;
5
1. コマンドをオプションを引数に取る関数として扱う
empty は空のオプション集合
add_l はオプション集合にl オプションを追加する関数
目標 2/5. 出力のレコード化
let collect_files size = let files = Ls.command (add_l empty) in let rec iter cur_size acc files = match files with | [] -> acc | file :: files -> let cur_size = cur_size + file..size in if cur_size > size then acc else iter cur_size (file..name :: acc) files in iter 0 [] files;;
6
2. コマンド出力が自動的にレコードに変換される
目標 3/5. レコード型の柔軟な変化
let collect_files size = let files = Ls.command (add_i (add_l empty)) in let rec iter cur_size acc files = match files with | [] -> acc | file :: files -> let cur_size = cur_size + file..size in if cur_size > size then acc else iter cur_size (file..name :: acc) files in iter 0 [] files;;
7
3. オプションが変化すると出力レコードの型も
適切に変化する
i オプションを追加して出力行レコードに inode という
int 型のフィールドを追加
目標 4/5. フィールドアクセスの検査
let collect_files size = let files = Ls.command (add_i empty) in let rec iter cur_size acc files = match files with | [] -> acc | file :: files -> let cur_size = cur_size + file..size in if cur_size > size then acc else iter cur_size (file..name :: acc) files in iter 0 [] files;;
8
4. ユーザがアクセスしたフィールドが存在することが検査できる
出力に size フィールドが無いので
コンパイルエラー
$ ls –i10358 a305049 b301392 c940030 d61038 e573013 f
目標 5/5. オプションの合成let collect_files opt size = let files = Ls.command (opt (add_l empty)) in let rec iter cur_size acc files = match files with | [] -> acc | file :: files -> let cur_size = cur_size + file..size in if cur_size > size then acc else iter cur_size (file..name :: acc) file in iter 0 [] files;;
9
5. オプションを合成可能にすること
collect_files add_t size collect_files add_S size
更新日時順
サイズ順
目標を実現する方針• 1. コマンドの関数化• 2. 出力のレコード化
10
• DSL によるコマンドの仕様の記述
• オプション集合をsingleton typeで表す出力行レコードに型レベル関数を用いる
• フィールドアクセスが安全であることの証明を生成
• 4.レコード型の柔軟な変化
• 3. フィールドの検査
• 5. オプションの合成 • オプション集合をタプルで表現
目標を実現する方針• 1. コマンドの関数化• 2. 出力のレコード化
11
• DSL によるコマンドの仕様の記述
• オプション集合をsingleton typeで表す出力行レコードに型レベル関数を用いる
• フィールドアクセスが安全であることの証明を生成
• 4.レコード型の柔軟な変化
• 3. フィールドの検査
• 5. オプションの合成 • オプション集合をタプルで表現
提案 1/2:OCommand DSL/関数と型定義の導出
• コマンドの形式を記述する領域特化言語(DSL)–オプションをコマンドに渡した時に , どのよ
うに形式が変化するかを記述• コマンドの記述からコマンドを OCaml 上
で型安全に扱うための関数 , 型定義を導出
12
DSL による形式の定義
13
• オプションの作用 : オプションが指定された時に出力の形式がどう変化するか
COMMAND ls : BEGIN ORDER {inode; owner; group; ...; name} OPTION i : ADD {inode : int} OPTION l : ADD {perm : perm; hard : int; ...} OPTION h : MODIFY {size : hsize} DEFAULT : {name : string}END
コマンドの名前フィールドの出現順序
オプションの作用
DSL の生成コード
14
• DSL で記述されたコマンドの仕様からコマンド関数の定義などが生成される
module Ls = struct ... let empty = ... let add_i opt = ... let add_l opt = ... let add_h opt = ... let command opts = ...end
コマンド関数
オプション集合を操作する関数の定義
目標を実現する方針• 1. コマンドの関数化• 2. 出力のレコード化
15
• DSL によるコマンドの仕様の記述
• オプション集合をsingleton typeで表す出力行レコードに型レベル関数を用いる
• フィールドアクセスが安全であることの証明を生成
• 4.レコード型の柔軟な変化
• 3. フィールドの検査
• 5. オプションの合成 • オプション型をタプルで表現
適切な型検査
• コマンド関数は引数のオプション集合によって出力行レコード型が異なる
16
# Ls.command empty- : {name : string} list
# Ls.command (add_i empty)- : {inode : int; name : string} list# Ls.command (add_i (add_l empty)) - : {inode : int; perm : …; name : string} list
引数の値毎に異なるレコード型を返す
提案 2/2:型レベル関数を用いたコマンド関数のエンコード
• オプション集合を GADTs によってsingleton type にする
• 出力行レコードに型レベル関数を用いる
17
オプション集合値
オプション集合型 出力行レコード型
一対一対応(singleton
type)
コマンド関数 :
依存
型レベル関数
→
提案 2/2:型レベル関数を用いたコマンド関数のエンコード
• オプション集合を GADTs によってsingleton type にする
• 出力行レコードに型レベル関数を用いる
• レコードをタプルで表現• perm, size, name 等が型レベル関数 18
('i opt_i * 'l opt_l *'h opt_h)
(('i, 'l, 'h) perm * ... * ('i, 'l, 'h) size * ... * ('i, 'l, 'h) name)) list
command : →
オプション指定型
• オプションが指定されているかどうかを表す型– GADTsを用いて定義
19
(* on, off は抽象型 *)type 'i opt_i = I_On : on opt_i | I_Off : off opt_ilet empty = (I_Off, L_Off, H_Off)
let add_i (_, l, h) = (I_On, l, h)let add_l (i, _, h) = (i, L_On, h)let add_h (i, l, _) = (i, l, H_On)
提案 2/2:型レベル関数を用いたコマンド関数のエンコード
• オプション集合を GADTs によってsingleton type にする
• 出力行レコードに型レベル関数を用いる
• レコードをタプルで表現– perm, size, name 等が型レベル関数 20
('i opt_i * 'l opt_l *'h opt_h)
(('i, 'l, 'h) perm * ... * ('i, 'l, 'h) size * ... * ('i, 'l, 'h) name)) list
command : →
フィールド型
• 各オプション指定型からフィールド出現型への型レベル関数–フィールド出現型 : フィールドが存在して
その型が τ であることを表す型 τ pre とフィールドが存在しないことを表す abs
• OCaml は型レベル関数を直接サポートしていないので何らかのエンコードが必要 21
'i 'l 'hsize (off, on, off) = int presize (off, on, on) = string presize (off, off, off) = abs
フィールド型
22
type ('i, 'l, 'h) size = | Size : 'fld field * ('i, 'l, 'h, 'fld) rel_size
type 'fld field =| FPre : 'a -> 'a pre field| FAbs : abs field
型引数 'fld は存在量化されて外から隠されている
'fld fieldは 'fldが preの時のみ値を持つ型 (GADTs)
フィールド型
• ∃x:(i, l, h, fld)rel_size ⇔ size(i,l,h)=fld
23
type ('i, 'l, 'h) size = | Size : 'fld field * ('i, 'l, 'h, 'fld) rel_size
type ('i, 'l, 'h, 'fld) rel_size =| Rel_size_0 : (off, off, off, abs) rel_size| Rel_size_1 : (off, off, on, abs) rel_size| Rel_size_2 : (off, on, off, int pre) rel_size| Rel_size_3 : (off, on, on, string pre) rel_size(* other 4 combinations of options *)
フィールド型
• x0 : (off, off, off) size ⇒x0 = (FAbs * Rel_size0)
• x1 : (off, on, off) size ⇒x1 = (FPre (int 型の値 ) * Rel_size2
24
type ('i, 'l, 'h) size = | Size : 'fld field * ('i, 'l, 'h, 'fld) rel_size
型引数毎に 'fld の型が変化している
目標を実現する方針• 1. コマンドの関数化• 2. 出力のレコード化
25
• DSL によるコマンドの仕様の記述
• オプション集合をsingleton typeで表す出力行レコードに型レベル関数を用いる
• フィールドアクセスが安全であることの証明を生成
• 4.レコード型の柔軟な変化
• 3. フィールドの検査
• 5. オプションの合成 • オプション集合をタプルで表現
フィールドアクセス≠パターンマッチ
• 型を無視するとパターンマッチで取り出せば良い
26
match file with| (..., Size (Fpre res, _), ...) -> res
file..size
存在量化された res の型がエスケープするので型エラー
フィールドアクセス = パターンマッチ + 証明
• 存在型が具体的なフィールド出現型に等しいことを証明して取り出す
27
match file with| (..., Size (x, rel), ...) -> let eq = eq_rel_size_off_on_off rel in let FPre res = apply_eq eq x in res
file..size
(off, on, off, ?) rel_size -> (?, int pre) eq
(?, int pre) eq -> ? field -> int pre field
実装
28
DSL で記述された
コマンドの形式
型 / 関数定義
フィールド出現型を証明する関数
コマンドを使うスクリプト
証明付きのスクリプト
型情報を得るためのダミーコード
ocamlコンパイラ
型情報(.annot)
合計 1000 行程度のシステムになった
コマンドモジュールの生成 (600 行 )
スクリプトの変換(200 行 )
ダミープログラムの生成 (100 行 )
事例研究• ls コマンドの他に ,df,ps コマンドを本
研究が提案する DSL で扱えることを確かめた
29
COMMAND ps : BEGIN ORDER : {f; s; uid; pid; ppid; c; pri;
ni; addr; sz; wchan; stime;
tty; time; cmd;} ;;
OPTION e : ADD {}
OPTION f : ADD {uid : string; ppid : int;
c : int; stime : time} ;;
OPTION l : ADD {f : int; s : string; uid : int;
ppid : int; c : int; pri : int;
ni : int; addr : string;
sz : int; wchan : string}
DEFAULT : {uid : int; tty : string; time : string;
cmd : string with delim = ""}
END;;
COMMAND df : BEGIN ORDER : {filesystem; type; size; used;
available; use; mounted_on} ;;
OPTION T : ADD {type : string};;
OPTION h : MODIFY {size : hint; use : hint;
avail : hint} ;;
DEFAULT : {filesystem : string; size : int;
used : int; available : int;
use : int; mouted_on : string}
END;;
フィールド数 :7対応したオプション :T,h フィールド数 :15
対応したオプション :e,f,l
関連研究 : Caml-Shcaml[Heller, A. and Tov, J. A.,
ML2008]• 手法–コマンドの形式をモジュールで記述–モジュールをファンクターに渡すことでコマン
ド関数等を生成• 本研究との相違点–オプションの合成等は扱っていない–出力行レコードへのアクセスに対する型安全性
はない
30
課題 : 多相なオプションへの対応
31
let sum_size opt size = let files = Ls.command (opt (add_l empty)) in List.fold files ~init:0 ~f:(fun acc file -> acc + file..size);;
• この時, optには“lオプションと組み合わせた時の出力行レコードには sizeというint型のフィールドが存在する”ような任意の型という型が付いてほしい
• OCommandエンコードでは表現できない
• 引数のオプションに対する制約はHaskellのtype classと関数従属性を用いて表現できる
結論
• コマンドの仕様を記述し、コマンドをOCaml 上で呼び出すためのコードを生成するための DSL を提案– オプションを指定した時にどのように出力を
変化させるかを記述• コンパイル時にフィールドアクセスが安全であ
ることを検査するための OCaml 上での型の表現を与えた– GADTs を用いて表現
• ls,ps,df が扱えることを確かめた• 多相なオプションをどう扱うかは今後の課題
32