32
OCommand : 型型型型 型型型型型型型型型型型型型型 型型型型型型型型型 型型 型 型型 型型 型型 型型 型型型型型型 型型 型型型型型型 1

OCommand : 型安全な シェルプログラミングのための 領域特化言語の提案

  • Upload
    soleil

  • View
    68

  • Download
    10

Embed Size (px)

DESCRIPTION

OCommand : 型安全な シェルプログラミングのための 領域特化言語の提案. 朝倉 泉 増原 英彦 青谷 知幸 東京工業大学 数理・計算 科学専攻. OCommand. OCaml 上 で安全にシェルコマンドを 呼び出すための処理 系 コマンドの仕様を書くための DSL コマンドの結果にアクセスする書式を変換する処理 系 コマンド 出力 への アクセス に対する 安全性 を保証. ls コマンドを利用するプログラム例. ls -l の出力からサイズ の合計 が与えられた閾値 より 小さいファイル 集合を求めたい 例 閾値 =10000Byte. - PowerPoint PPT Presentation

Citation preview

Page 1: OCommand  :  型安全な シェルプログラミングのための 領域特化言語の提案

OCommand : 型安全なシェルプログラミングのための

領域特化言語の提案

朝倉 泉増原 英彦青谷 知幸

東京工業大学 数理・計算科学専攻1

Page 2: OCommand  :  型安全な シェルプログラミングのための 領域特化言語の提案

OCommand• OCaml 上で安全にシェルコマンドを

呼び出すための処理系–コマンドの仕様を書くための DSL–コマンドの結果にアクセスする書式を変換す

る処理系• コマンド出力へのアクセスに対する

安全性を保証

2

Page 3: OCommand  :  型安全な シェルプログラミングのための 領域特化言語の提案

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}

各欄が適切なデータ型に変換されたレコード型として扱いたい

Page 4: OCommand  :  型安全な シェルプログラミングのための 領域特化言語の提案

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

指定されているオプションの集合

( オプション集合 )

Page 5: OCommand  :  型安全な シェルプログラミングのための 領域特化言語の提案

目標 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 オプションを追加する関数

Page 6: OCommand  :  型安全な シェルプログラミングのための 領域特化言語の提案

目標 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. コマンド出力が自動的にレコードに変換される

Page 7: OCommand  :  型安全な シェルプログラミングのための 領域特化言語の提案

目標 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 型のフィールドを追加

Page 8: OCommand  :  型安全な シェルプログラミングのための 領域特化言語の提案

目標 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

Page 9: OCommand  :  型安全な シェルプログラミングのための 領域特化言語の提案

目標 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

更新日時順

サイズ順

Page 10: OCommand  :  型安全な シェルプログラミングのための 領域特化言語の提案

目標を実現する方針• 1. コマンドの関数化• 2. 出力のレコード化

10

• DSL によるコマンドの仕様の記述

• オプション集合をsingleton typeで表す出力行レコードに型レベル関数を用いる

• フィールドアクセスが安全であることの証明を生成

• 4.レコード型の柔軟な変化

• 3. フィールドの検査

• 5. オプションの合成 • オプション集合をタプルで表現

Page 11: OCommand  :  型安全な シェルプログラミングのための 領域特化言語の提案

目標を実現する方針• 1. コマンドの関数化• 2. 出力のレコード化

11

• DSL によるコマンドの仕様の記述

• オプション集合をsingleton typeで表す出力行レコードに型レベル関数を用いる

• フィールドアクセスが安全であることの証明を生成

• 4.レコード型の柔軟な変化

• 3. フィールドの検査

• 5. オプションの合成 • オプション集合をタプルで表現

Page 12: OCommand  :  型安全な シェルプログラミングのための 領域特化言語の提案

提案 1/2:OCommand DSL/関数と型定義の導出

• コマンドの形式を記述する領域特化言語(DSL)–オプションをコマンドに渡した時に , どのよ

うに形式が変化するかを記述• コマンドの記述からコマンドを OCaml 上

で型安全に扱うための関数 , 型定義を導出

12

Page 13: OCommand  :  型安全な シェルプログラミングのための 領域特化言語の提案

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

コマンドの名前フィールドの出現順序

オプションの作用

Page 14: OCommand  :  型安全な シェルプログラミングのための 領域特化言語の提案

DSL の生成コード

14

• DSL で記述されたコマンドの仕様からコマンド関数の定義などが生成される

module Ls = struct ... let empty = ... let add_i opt = ... let add_l opt = ... let add_h opt = ... let command opts = ...end

コマンド関数

オプション集合を操作する関数の定義

Page 15: OCommand  :  型安全な シェルプログラミングのための 領域特化言語の提案

目標を実現する方針• 1. コマンドの関数化• 2. 出力のレコード化

15

• DSL によるコマンドの仕様の記述

• オプション集合をsingleton typeで表す出力行レコードに型レベル関数を用いる

• フィールドアクセスが安全であることの証明を生成

• 4.レコード型の柔軟な変化

• 3. フィールドの検査

• 5. オプションの合成 • オプション型をタプルで表現

Page 16: OCommand  :  型安全な シェルプログラミングのための 領域特化言語の提案

適切な型検査

• コマンド関数は引数のオプション集合によって出力行レコード型が異なる

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

引数の値毎に異なるレコード型を返す

Page 17: OCommand  :  型安全な シェルプログラミングのための 領域特化言語の提案

提案 2/2:型レベル関数を用いたコマンド関数のエンコード

• オプション集合を GADTs によってsingleton type にする

• 出力行レコードに型レベル関数を用いる

17

オプション集合値

オプション集合型 出力行レコード型

一対一対応(singleton

type)

コマンド関数 :

依存

型レベル関数

Page 18: OCommand  :  型安全な シェルプログラミングのための 領域特化言語の提案

提案 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 : →

Page 19: OCommand  :  型安全な シェルプログラミングのための 領域特化言語の提案

オプション指定型

• オプションが指定されているかどうかを表す型– 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)

Page 20: OCommand  :  型安全な シェルプログラミングのための 領域特化言語の提案

提案 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 : →

Page 21: OCommand  :  型安全な シェルプログラミングのための 領域特化言語の提案

フィールド型

• 各オプション指定型からフィールド出現型への型レベル関数–フィールド出現型 : フィールドが存在して

その型が τ であることを表す型 τ pre とフィールドが存在しないことを表す abs

• OCaml は型レベル関数を直接サポートしていないので何らかのエンコードが必要 21

'i 'l 'hsize (off, on, off) = int presize (off, on, on) = string presize (off, off, off) = abs

Page 22: OCommand  :  型安全な シェルプログラミングのための 領域特化言語の提案

フィールド型

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)

Page 23: OCommand  :  型安全な シェルプログラミングのための 領域特化言語の提案

フィールド型

• ∃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 *)

Page 24: OCommand  :  型安全な シェルプログラミングのための 領域特化言語の提案

フィールド型

• 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 の型が変化している

Page 25: OCommand  :  型安全な シェルプログラミングのための 領域特化言語の提案

目標を実現する方針• 1. コマンドの関数化• 2. 出力のレコード化

25

• DSL によるコマンドの仕様の記述

• オプション集合をsingleton typeで表す出力行レコードに型レベル関数を用いる

• フィールドアクセスが安全であることの証明を生成

• 4.レコード型の柔軟な変化

• 3. フィールドの検査

• 5. オプションの合成 • オプション集合をタプルで表現

Page 26: OCommand  :  型安全な シェルプログラミングのための 領域特化言語の提案

フィールドアクセス≠パターンマッチ

• 型を無視するとパターンマッチで取り出せば良い

26

match file with| (..., Size (Fpre res, _), ...) -> res

file..size

存在量化された res の型がエスケープするので型エラー

Page 27: OCommand  :  型安全な シェルプログラミングのための 領域特化言語の提案

フィールドアクセス = パターンマッチ + 証明

• 存在型が具体的なフィールド出現型に等しいことを証明して取り出す

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

Page 28: OCommand  :  型安全な シェルプログラミングのための 領域特化言語の提案

実装

28

DSL で記述された

コマンドの形式

型 / 関数定義

フィールド出現型を証明する関数

コマンドを使うスクリプト

証明付きのスクリプト

型情報を得るためのダミーコード

ocamlコンパイラ

型情報(.annot)

合計 1000 行程度のシステムになった

コマンドモジュールの生成 (600 行 )

スクリプトの変換(200 行 )

ダミープログラムの生成 (100 行 )

Page 29: OCommand  :  型安全な シェルプログラミングのための 領域特化言語の提案

事例研究• 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

Page 30: OCommand  :  型安全な シェルプログラミングのための 領域特化言語の提案

関連研究 : Caml-Shcaml[Heller, A. and Tov, J. A.,

ML2008]• 手法–コマンドの形式をモジュールで記述–モジュールをファンクターに渡すことでコマン

ド関数等を生成• 本研究との相違点–オプションの合成等は扱っていない–出力行レコードへのアクセスに対する型安全性

はない

30

Page 31: OCommand  :  型安全な シェルプログラミングのための 領域特化言語の提案

課題 : 多相なオプションへの対応

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と関数従属性を用いて表現できる

Page 32: OCommand  :  型安全な シェルプログラミングのための 領域特化言語の提案

結論

• コマンドの仕様を記述し、コマンドをOCaml 上で呼び出すためのコードを生成するための DSL を提案– オプションを指定した時にどのように出力を

変化させるかを記述• コンパイル時にフィールドアクセスが安全であ

ることを検査するための OCaml 上での型の表現を与えた– GADTs を用いて表現

• ls,ps,df が扱えることを確かめた• 多相なオプションをどう扱うかは今後の課題

32