定理証明器 Coq と
証明付き DSL
2010/11/27
@tmiya_
あなたがCoqを学ぶべき理由
2	
どう考えても成り立つのは自明だけど、
総当たりで示すには状態爆発してしまう
テストで発見出来るのは間違いだけ。
正しい事は保証出来ないよ
網羅性のあるテストを書くのも、
何回も実施するのも大変だ
そんなあなたには証明駆動開発。
正しいと証明されたコードを生成。
全ケースを網羅しないと証明通らない。
一度証明したらテストは不要。
まさに定理証明系向きの課題。
正しい事を証明しよう。
最近はDSLが流行っているようだけど
オレオレ言語に信頼性あるのかなぁ?
正しさを保証する言語処理系を作るに
は定理証明系を使うのが一番
Domain
Specific
Languages
3
ドメイン固有言語
•  Domain Specific Languages
•  特定分野のタスクの為に作られた言語
•  利点
–  問題領域に適した抽象化、用語を使用
–  読みやすい/書きやすい/ドキュメントの代わり
•  自然言語より簡潔かつ厳密に記述可能
•  スクリプト言語(Ruby, Pythonなど)が使われる事多い
–  安全性
•  利用範囲を超えた機能は提供されない
•  それだけで満足?正しさをどう担保する?
–  DSLで書かれたものの正しさ。DSLそれ自体の正しさ。
•  Mechanized metatheory: ready for prime time? (ICFP 2010)
–  http://www.cis.upenn.edu/ bcpierce/wmm/wmm10/cheney.pdf
–  これまで:既にあるもの(言語とか)を定理証明系で検証
–  これから:定理証明系の存在を前提に言語のデザイン(例えば次の
XQueryとか)
金融商品DSL
•  発端
–  2010/4/7 : SECが金融商品を明確に記述する為に、自然言語
ではなくPythonを使う事を提案
–  なぜPython?もっと相応しい物があるのでは?例えば…
•  型付き関数型言語:型検査でDSLの安全性を担保可能
–  「金融取引契約の書き方」(Simon Peyton Jones and Jean-Marc Eber)
•  書籍「関数プログラミング言語の楽しみ」
•  金融取引はコンビネータを組み合わせて記述可能。
–  規格にあるものしか書けない金融商品 XML (FpML) は仕様が肥大化
するしか無い。
•  Haskellでライブラリ記述。
•  後にLexiFi社のMLFiに
•  ACMよりのSECへのコメント
–  http://www.sec.gov/comments/s7-08-10/s70810-89.pdf
–  「Pythonより適切な言語がある。静的型検査可能でsandboxがあるものが良
い」
–  「専用の適切なDSLあるいはライブラリを開発すべき」
–  まぁ当然の解答と言える
AURA:認可•監査論理の言語
•  論文: AURA: A Programming Language for
Authorization and Audit  (2008 ICFP)
•  「∼は∼をしても良い」を記述するDSL
–  依存型のある言語では静的型検査で証明を扱える
  「コンパイル時に」アクセス権限有無を判定可能
–  権限委譲は関数合成(モナド)として表現可能
•  AURAの実装
–  ある種の型付きλ計算として定義 (1.4k LOC)
–  soundness, decidability を Coq で証明 (6k + 5k
LOC)
 AURA言語の正しさを証明
–  F#での実装が入手可能
•  http://www.cis.upenn.edu/ stevez/sol/aura.html
Compcert Verified Compiler
•  http://compcert.inria.fr/
•  Coqで証明されたC言語コンパイラ
–  Compcert C PowerPC
•  Cのsubset : longjmp/setjmpとか一部サポートされない
–  生成されたコードは元のCコードと等価である事を証明
–  実用を考えた最適化:gcc ‒O1並みの速度
•  将来の応用
7	
Program	
  Prover	
  
Model	
  Checker	
  Sta3c	
  Analyzer	
  
Other	
  Language	
  
Clight	
  
Cminor	
  
External vs Internal
•  外部DSLの話 : AURA
–  汎用的なプログラミング言語とは別に、DSL言語を作る
•  単なるXMLファイルという場合もあるが
•  コードジェネレーションなんかもここに入る
–  どうせならCoqとかを使って言語の正しさを証明したい
–  Operational Semantics : とりあえずTAPL勉強中
•  Ott : そういうツールがあるらしい。使い方を覚えたいなぁ。
•  内部DSL : 金融商品コンビネータの話
–  汎用プログラミング言語自体をDSLとして使える様にする
•  世間での評価「古くはLispで、最近はRubyとかで」
•  Haskellで内部DSLに、という話は昔から多い。(最近はScalaも割と)
•  要するにプログラム言語っぽく無くて、自然言語や専用言語っぽければOK
•  CoqでDSLってどうだろう?
–  実はCoqは向いているのでは?
Coq as a
DSL Host
Language
9
記法を定義出来る (1)
•  Coqではユーザ定義の記法を使える
Notation "A / B" := (and A B) (at level 80, right associativity).
•  表示のときも使ってくれる
Coq < Check (and True False).
True / False
: Prop
•  記号の意味を探すときはLocate
Coq < Locate "/".
Notation Scope
"A / B" := and A B : type_scope
(default interpretation)
10	
演算子 優先度
/ 85(低)	
/ 80	
~ 75	
=, <>, <, >,
<=, >=
70	
+, -, || 50	
*, /, && 40	
- (単項の-) 35(高)	
これを参考に
記法を定義出来る (2)
•  単項演算子、mix-fixな演算子もOK : かなりDSLっぽくなる
Notation "~ x" := (not x) (at level 75, right associativity).
Notation "’IF’ c1 ’then’ c2 ’else’ c3" :=
(IF_then_else c1 c2 c3).
Notation "{ x : A | P }” := (sigA (fun x => P))
(at level 0, x at level 99).
•  推論して欲しい場合は@を付けないと(使用時に)エラー
Notation "x = y" := (@eq _ x y) (at level 70, no associativity).
•  定義と一緒に記法を定義出来る
Fixpoint plus (n m:nat) {struct n} : nat :=
match n with
| O => m
| S p => S (p+m)
  end
where "n + m" := (plus n m).
11
記法を定義出来る (3)
•  パーサコンビネータと組み合わせて使っている例
http://www.seas.upenn.edu/~cis500/current/sf/html/ImpParser.html
Notation “‘DO’ ( x , y ) <-- e1 ;; e2 ‘OR’ e3” :=
  (match e1 with
| SomeE (x,y) => e2
| NoneE err => e3
end) (right associativity, at level 60, e2 at next level).
•  使っている例
Fixpoint parsePrimaryExp (steps:nat) symtable (xs : list token)
: optionE (aexp * list token) :=
match steps with
| 0 => NoneE "Too many recursive calls"
| S steps' =>
DO (i, rest) <-- parseIdentifier symtable xs ;;
SomeE (AId i, rest)
OR DO (n, rest) <-- parseNumber xs ;;
SomeE (ANum n, rest)
OR (DO (e, rest) <== firstExpect "(" (parseSumExp steps' symtable) xs;;
DO (u, rest') <== expect ")" rest ;;
SomeE(e,rest'))
end 12
記法を定義出来る (4)
•  繰り返しのあるパターン : .. を使って繰り返しを再帰的に表現出来る
–  可変長引数みたいなものを扱いたいときは便利
Coq < Notation "[ x ; .. ; y ]" :=
  (@cons _ x .. (@cons _ y (@nil _) ) .. ).
Setting notation at level 0.
Coq < Check ([1 ; 2 ; 3]).
[1; 2; 3]
: list nat
•  実は下記でもOK
Coq < Notation "[ x ; .. ; y ]" := (cons x .. (cons y nil) ..).
Setting notation at level 0.
13
記法定義の落とし穴
•  えっ?定義が無いの?本当?
Coq < Locate "->".
Unknown notation
•  定義出来た!?
Notation "A -> B" := (and A B) (at level 80, right associativity).
•  本当に定義が上書きされたのか?
Coq < Lemma test : True -> True.
test < intros.
1 subgoal
============================
True -> True  (* 変化が無い *)
test < split.
2 subgoals
============================
True  (* and なので split される *)
subgoal 2 is:
True
•  Locate信用出来ない。重要な予約語の上書きには注意。 14
Tacticの定義の方法
CPDTに載っているTactic定義の例 (p.217)
Coq < Ltac my_tauto :=
Coq < repeat match goal with
Coq < | [ H : ?P |- ?P ] => exact H
Coq < | [ |- True ] => constructor
Coq < | [ |- _ / _ ] => constructor
Coq < | [ |- _ -> _ ] => intro
Coq < | [ H : False |- _ ] => destruct H
Coq < | [ H : _ / _ |- _ ] => destruct H
Coq < | [ H : _ / _ |- _ ] => destruct H
Coq < | [ H1 : ?P -> ?Q, H2 : ?P |- _ ] =>
Coq < let H := fresh “H” in (* Hなんとかみたいな新しい名前をつける *)
Coq < generalize (H1 H2); clear H1; intro H
Coq < end.
my_tauto is defined
repeatを外したmy_tauto1を作って、何度も適用すると動作の具合が良く解る
…これって、DSLに使えない?
•  パターンマッチとかどう見ても高機能なんだが…
15
Ltac定義の中で出来る事
•  順次処理(expr1; expr2)
•  繰り返し(repeat expr)(do num expr)
•  ローカル定義(let)
•  パターンマッチ(match)
•  計算(eval redexpr in term)
•  メッセージ表示(idtac msg_token … msg_token)
•  外部呼び出し(external “command” “request” tacarg…)
–  元々、外部の定理証明器を呼び出す為の機能
–  XML形式で外と通信出来る
–  これを使って外部コマンドを実行出来たらどうか?
–  例えば “ls” コマンドを実行して、ファイルリストをCoqに返せたら?
–  makeの真似事ぐらい出来そうな…
と、考えてみたんですが、そこで行き詰まりました。
•  Coq内部とかOcamlに詳しい人が引き継いでくれるといいなぁ…
•  気をつけないと矛盾をCoqに持ち込んでFalsoみたいになるかも…
16
まとめ
•  Domain Specific Languages
–  やっぱり正しさが証明されているDSLを使いたいですよね。
–  ならばCoqでDSLを。
•  Coq as a DSL Host Language
–  CoqのNotation定義で自由なDSL記法を
–  Tacticから外部コマンド実行とか出来ると、色々出来るんじゃ
ないかな?
17

Coq Party 20101127

  • 1.
  • 2.
  • 3.
  • 4.
    ドメイン固有言語 •  Domain SpecificLanguages •  特定分野のタスクの為に作られた言語 •  利点 –  問題領域に適した抽象化、用語を使用 –  読みやすい/書きやすい/ドキュメントの代わり •  自然言語より簡潔かつ厳密に記述可能 •  スクリプト言語(Ruby, Pythonなど)が使われる事多い –  安全性 •  利用範囲を超えた機能は提供されない •  それだけで満足?正しさをどう担保する? –  DSLで書かれたものの正しさ。DSLそれ自体の正しさ。 •  Mechanized metatheory: ready for prime time? (ICFP 2010) –  http://www.cis.upenn.edu/ bcpierce/wmm/wmm10/cheney.pdf –  これまで:既にあるもの(言語とか)を定理証明系で検証 –  これから:定理証明系の存在を前提に言語のデザイン(例えば次の XQueryとか)
  • 5.
    金融商品DSL •  発端 –  2010/4/7: SECが金融商品を明確に記述する為に、自然言語 ではなくPythonを使う事を提案 –  なぜPython?もっと相応しい物があるのでは?例えば… •  型付き関数型言語:型検査でDSLの安全性を担保可能 –  「金融取引契約の書き方」(Simon Peyton Jones and Jean-Marc Eber) •  書籍「関数プログラミング言語の楽しみ」 •  金融取引はコンビネータを組み合わせて記述可能。 –  規格にあるものしか書けない金融商品 XML (FpML) は仕様が肥大化 するしか無い。 •  Haskellでライブラリ記述。 •  後にLexiFi社のMLFiに •  ACMよりのSECへのコメント –  http://www.sec.gov/comments/s7-08-10/s70810-89.pdf –  「Pythonより適切な言語がある。静的型検査可能でsandboxがあるものが良 い」 –  「専用の適切なDSLあるいはライブラリを開発すべき」 –  まぁ当然の解答と言える
  • 6.
    AURA:認可•監査論理の言語 •  論文: AURA:A Programming Language for Authorization and Audit  (2008 ICFP) •  「∼は∼をしても良い」を記述するDSL –  依存型のある言語では静的型検査で証明を扱える   「コンパイル時に」アクセス権限有無を判定可能 –  権限委譲は関数合成(モナド)として表現可能 •  AURAの実装 –  ある種の型付きλ計算として定義 (1.4k LOC) –  soundness, decidability を Coq で証明 (6k + 5k LOC)  AURA言語の正しさを証明 –  F#での実装が入手可能 •  http://www.cis.upenn.edu/ stevez/sol/aura.html
  • 7.
    Compcert Verified Compiler • http://compcert.inria.fr/ •  Coqで証明されたC言語コンパイラ –  Compcert C PowerPC •  Cのsubset : longjmp/setjmpとか一部サポートされない –  生成されたコードは元のCコードと等価である事を証明 –  実用を考えた最適化:gcc ‒O1並みの速度 •  将来の応用 7 Program  Prover   Model  Checker  Sta3c  Analyzer   Other  Language   Clight   Cminor  
  • 8.
    External vs Internal • 外部DSLの話 : AURA –  汎用的なプログラミング言語とは別に、DSL言語を作る •  単なるXMLファイルという場合もあるが •  コードジェネレーションなんかもここに入る –  どうせならCoqとかを使って言語の正しさを証明したい –  Operational Semantics : とりあえずTAPL勉強中 •  Ott : そういうツールがあるらしい。使い方を覚えたいなぁ。 •  内部DSL : 金融商品コンビネータの話 –  汎用プログラミング言語自体をDSLとして使える様にする •  世間での評価「古くはLispで、最近はRubyとかで」 •  Haskellで内部DSLに、という話は昔から多い。(最近はScalaも割と) •  要するにプログラム言語っぽく無くて、自然言語や専用言語っぽければOK •  CoqでDSLってどうだろう? –  実はCoqは向いているのでは?
  • 9.
    Coq as a DSLHost Language 9
  • 10.
    記法を定義出来る (1) •  Coqではユーザ定義の記法を使える Notation "A / B" := (and A B) (at level 80, right associativity). • 表示のときも使ってくれる Coq < Check (and True False). True / False : Prop •  記号の意味を探すときはLocate Coq < Locate "/". Notation Scope "A / B" := and A B : type_scope (default interpretation) 10 演算子 優先度 / 85(低) / 80 ~ 75 =, <>, <, >, <=, >= 70 +, -, || 50 *, /, && 40 - (単項の-) 35(高) これを参考に
  • 11.
    記法を定義出来る (2) •  単項演算子、mix-fixな演算子もOK: かなりDSLっぽくなる Notation "~ x" := (not x) (at level 75, right associativity). Notation "’IF’ c1 ’then’ c2 ’else’ c3" := (IF_then_else c1 c2 c3). Notation "{ x : A | P }” := (sigA (fun x => P)) (at level 0, x at level 99). •  推論して欲しい場合は@を付けないと(使用時に)エラー Notation "x = y" := (@eq _ x y) (at level 70, no associativity). •  定義と一緒に記法を定義出来る Fixpoint plus (n m:nat) {struct n} : nat := match n with | O => m | S p => S (p+m)   end where "n + m" := (plus n m). 11
  • 12.
    記法を定義出来る (3) •  パーサコンビネータと組み合わせて使っている例 http://www.seas.upenn.edu/~cis500/current/sf/html/ImpParser.html Notation“‘DO’ ( x , y ) <-- e1 ;; e2 ‘OR’ e3” :=   (match e1 with | SomeE (x,y) => e2 | NoneE err => e3 end) (right associativity, at level 60, e2 at next level). •  使っている例 Fixpoint parsePrimaryExp (steps:nat) symtable (xs : list token) : optionE (aexp * list token) := match steps with | 0 => NoneE "Too many recursive calls" | S steps' => DO (i, rest) <-- parseIdentifier symtable xs ;; SomeE (AId i, rest) OR DO (n, rest) <-- parseNumber xs ;; SomeE (ANum n, rest) OR (DO (e, rest) <== firstExpect "(" (parseSumExp steps' symtable) xs;; DO (u, rest') <== expect ")" rest ;; SomeE(e,rest')) end 12
  • 13.
    記法を定義出来る (4) •  繰り返しのあるパターン: .. を使って繰り返しを再帰的に表現出来る –  可変長引数みたいなものを扱いたいときは便利 Coq < Notation "[ x ; .. ; y ]" :=   (@cons _ x .. (@cons _ y (@nil _) ) .. ). Setting notation at level 0. Coq < Check ([1 ; 2 ; 3]). [1; 2; 3] : list nat •  実は下記でもOK Coq < Notation "[ x ; .. ; y ]" := (cons x .. (cons y nil) ..). Setting notation at level 0. 13
  • 14.
    記法定義の落とし穴 •  えっ?定義が無いの?本当? Coq <Locate "->". Unknown notation •  定義出来た!? Notation "A -> B" := (and A B) (at level 80, right associativity). •  本当に定義が上書きされたのか? Coq < Lemma test : True -> True. test < intros. 1 subgoal ============================ True -> True  (* 変化が無い *) test < split. 2 subgoals ============================ True  (* and なので split される *) subgoal 2 is: True •  Locate信用出来ない。重要な予約語の上書きには注意。 14
  • 15.
    Tacticの定義の方法 CPDTに載っているTactic定義の例 (p.217) Coq <Ltac my_tauto := Coq < repeat match goal with Coq < | [ H : ?P |- ?P ] => exact H Coq < | [ |- True ] => constructor Coq < | [ |- _ / _ ] => constructor Coq < | [ |- _ -> _ ] => intro Coq < | [ H : False |- _ ] => destruct H Coq < | [ H : _ / _ |- _ ] => destruct H Coq < | [ H : _ / _ |- _ ] => destruct H Coq < | [ H1 : ?P -> ?Q, H2 : ?P |- _ ] => Coq < let H := fresh “H” in (* Hなんとかみたいな新しい名前をつける *) Coq < generalize (H1 H2); clear H1; intro H Coq < end. my_tauto is defined repeatを外したmy_tauto1を作って、何度も適用すると動作の具合が良く解る …これって、DSLに使えない? •  パターンマッチとかどう見ても高機能なんだが… 15
  • 16.
    Ltac定義の中で出来る事 •  順次処理(expr1; expr2) • 繰り返し(repeat expr)(do num expr) •  ローカル定義(let) •  パターンマッチ(match) •  計算(eval redexpr in term) •  メッセージ表示(idtac msg_token … msg_token) •  外部呼び出し(external “command” “request” tacarg…) –  元々、外部の定理証明器を呼び出す為の機能 –  XML形式で外と通信出来る –  これを使って外部コマンドを実行出来たらどうか? –  例えば “ls” コマンドを実行して、ファイルリストをCoqに返せたら? –  makeの真似事ぐらい出来そうな… と、考えてみたんですが、そこで行き詰まりました。 •  Coq内部とかOcamlに詳しい人が引き継いでくれるといいなぁ… •  気をつけないと矛盾をCoqに持ち込んでFalsoみたいになるかも… 16
  • 17.
    まとめ •  Domain SpecificLanguages –  やっぱり正しさが証明されているDSLを使いたいですよね。 –  ならばCoqでDSLを。 •  Coq as a DSL Host Language –  CoqのNotation定義で自由なDSL記法を –  Tacticから外部コマンド実行とか出来ると、色々出来るんじゃ ないかな? 17