62
http://github.com/KDXU/InferAgda Agdaによる型推論器の定式化 お茶の水女子大学大学院 門脇香子,浅井健一 [email protected] [email protected] 1

Agda による型推論器の定式化

Embed Size (px)

Citation preview

Page 1: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda

Agdaによる型推論器の定式化

お茶の水女子大学大学院 門脇香子,浅井健一 [email protected] [email protected]

1

Page 2: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda

研究概要• Agda を用いて停止性と正当性がそれぞれ保証された型推論器を pure functional に実装した

• unification の実装部分は McBride の手法を採用した

• 主に unification の部分と application の実装にフォーカスを当てながら解説します

• コード : http://github.com/KDXU/InferAgda

2

Page 3: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda

Agdaについて

• 依存型を用いた,Haskell に似た構文をもつ定理証明支援系言語

• プログラミング言語と定理証明支援系言語の両面を持っている → 何かを実装しつつ証明をするのに適している

• マルティンレフの型理論に基づいている

3

Page 4: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda

発表の流れ1. 研究概要

2. 研究背景

3. シンタックスと型定義

4. unification の実装

5. well-scoped term における実装

6. well-typed term における実装

7. まとめ

4

Page 5: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda

研究背景• 依存型を使うと対象言語の型を項の定義に埋め込むことができる

• しかし,これまでの依存型による型チェックは,束縛変数の型があらかじめユーザによって入力されている→ 型推論は行わない!

• 型の unification を Agda で表現するのは難しかった

• McBride は停止性が明らかな形で型の unification が表現できることを示した→ Agda で型推論器を実装する土台が整った

5

Page 6: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda

シンタックスと型定義• シンタックス自由変数の数がn個のwell-scoped term

• 依存型を用いた de Bruijn index

6

Page 7: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda

依存型• 値に依存する型のこと

• 「Int 型を持つ項」なども表現できるようになる

• さまざまな面白い応用が知られている

7

List n 型 … 「長さ n のリスト」という意味を持つ型

Page 8: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda

シンタックスと型定義

• 型定義

8

Page 9: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda

unification• McBride の手法を Agda で実装したもの

• unification は書き換え可能なセルを使うと実装できる→ Agda だと書き換えは許されない & 停止性が保証されている必要がある

9

Page 10: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda

unification• McBride の手法を Agda で実装したもの

• unification は書き換え可能なセルを使うと実装できる→ Agda だと書き換えは許されない & 停止性が保証されている必要がある

10

そこでMcBrideは「型変数が具体化されるたびに,型変数の数が一つ減る」という点に注目

Page 11: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda

unification

• n 個の「具体化されていない型変数」を 0 から n-1までの数字からなる集合の型を表す Fin n 型で表す

• その n を減らすことで停止性が明らかな形の unification を実現

11

Page 12: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda

unification

12

Fin 2Fin 3

=

Page 13: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda

unification の実装の流れ

1. 変数を濃縮する関数 thick を実装 2. 変数の出現を確認する check を実装 3. 代入を表すデータ構造 AList を定義 4. mgu を定義

13

Page 14: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda

thick 関数• 変数 y を x の位置で「濃縮」する関数 • 例 : x = 1 で濃縮 y = 1 だと濃縮できない

14

Fin 2 Fin 1

Page 15: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda

thick 関数

15

Page 16: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda

check 関数check x t : x 番の型変数が型 t の中に現れるかを thick を用いてチェックする

16

Page 17: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda

AList 型• AList m n :「m 個の型変数を持つ型」を「n 個の型変数を持つ型」に変換するような代入の型 (m, n は m ≥ n であるような自然数)

17

Page 18: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda

flexFlex,flexRigid

• flexFlex x y : 型変数 x と y を単一化する代入

18

Page 19: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda

flexFlex,flexRigid

• flexRigid : 型変数 x と 型 t を単一化する代入

19

Page 20: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda

mgu 関数

• 型 s と型 t を同じにするような代入を返す

• mgu は結果として (n, 代入) を返す

• 代入は m 個の型変数を n 個まで減らす

• amgu : accumulator を使って実装

20

Page 21: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda

•型 s と t の構造に従って素直に unification を行う •m についての構造的帰納法を用いている •片方,または両方が型変数だった場合は flexRigid, flexFlex を使って unification を行っている

• (r for z) ◁ s は型 s の中の型変数全てについて (r for z) を施した型

21

Page 22: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda

unification

22

• 以上で unify t1 t2 は型変数が m 個であるような型 t1 と t2 を unify する代入を返す関数として実装ができた

Page 23: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda

well-scoped な型推論

. infer Γ s : 型環境 Γ と well-scoped な項 s をもらってき たら, s の型と必要な代入を Maybe モナドに包んで返す

23

σ Γ ⊢ s : τ

入力

出力

Page 24: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda

well-scoped な型推論

24

count : Lam と App のノード数を数える関数

Page 25: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda

variable

• 変数の場合は単に型環境の型を返す

25

Page 26: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda

lambda• まず body 部分の型推論を行い,body 部分に型が付いた場合のみ型付けされ,「引数の型⇒ body の型」が全体の型となる

26

Page 27: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda

application

27

1. App s1 s2 において,s1 の型推論によって返された代入 σ1 を Γ に加え,

2. 環境 σ1 Γ の元 での s2 の型推論が成功した場合にのみ,

3. (s2 の型⇒ β)が s1 と等しくなっているかどうかを unify 関数によって判定する

Page 28: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda

application

28

Page 29: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda

application

29

1. はじめに再帰呼び出しから infer {m} Γ s1 を行うと σ1 Γ ⊢ s1 : t1 なる σ1, t1 が得られる.

2. 次に s2 についての再帰で,環境 σ1 Γ の元で s1 の型は infer {m1} Γ s2 となるので, σ2(σ1Γ) ⊢ s2 : t2 なる σ2, t2 が得られる.

1. ここで得られた σ2 を 1. で得られた型判定に適用すると,

Page 30: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda

application

30

4. 次に,σ2(t1) = t2 → β (ここで β は新しい型変数)となる unify σ3 を求めると, σ3(σ2(t1)) = σ3(t2 → β)

となり,σ3 を 2. で得られているふたつの型判定に適用すると, σ3 (σ2 (σ1 Γ)) ⊢ s1 : σ3 (σ2(t1)) σ3 (σ2(σ1 Γ)) ⊢ s2 : σ3 (t2)

が得られる.

Page 31: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda

application

31

5.ここから関数適用の型規則を使うと,

σ3(σ2(σ1Γ)) ⊢ App s1 s2 : σ3(β)

が得られる.6.よって,App s1 s2 が返すものは

just (m2,σ3+(σ2+σ1), σ3(β))

となる.

Page 32: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda

application

32

型変数の数を巧妙にとらえることがキーとなる

Page 33: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda

application

33

❉ 型変数の数は,型推論が始まる時点では m である ❉ s1 を型推論する時点でそれは m+count s1 まで増えるが型 推論中に型変数の具体化が起こり m1 となる ❉ 次に,s2 を型推論する時点で m1+count s2 まで増えるがや はり型推論中に型変数の具体化が起こり m2 となる ❉ さらに,β を割り当てるので m2 + 1 個に増え, ❉ 最後の unification で m3 個に減ることになる

Page 34: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda

well-typed な型推論• これまでの型推論器は停止性のみ保証している

• 正当性が保証された型推論をしたい

• well-typed な型を定義する

• 型規則を構文の定義に埋め込んでいる

34

Page 35: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda

well-typed な型推論

• infer2 :型環境と well-scoped な項 s が入力となり,型変数の数 m′ と s の型 τ と必要な代入 σ と型付けされた well-typed な項を Maybe モナドに包んで返す関数

35

Page 36: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda

variable• 変数の場合は単に型環境の型を返す

• Var x の型が微妙に違うので補助関数を用いて結んでいる

36

Page 37: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda

lambda• まず body 部分の型推論を行い,body 部分に型が付いた場合のみ型付けされ,「引数の型⇒ body の型」が全体の型となる

37

Page 38: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda

lambda• well-scoped の場合と同様,まず body 部分の型推論を行い,body 部分に型が付いた場合のみ型付けされ,「引数の型⇒ body の型」が全体の型となる

38

Page 39: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda

lambda

39

• このコードは well-scoped な場合と比べて,再帰呼び出しの結果として well-typed な項を受け取っていることと,最後に well-typed な LamW を返している点のみが異なっている

• body 部分を型推論しwell-typed な w が得られた場合は最終的に返したい well-typed な項はほぼ Lam tx w となる

• しかし,実際に再帰呼び出しで得られた w の型が異なる

Page 40: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda

lambda

40

wの型は展開すると

WellTypedTerm (tx :: substCxt σ (liftCxt (count s) (liftCxt 1 Γ))) t

であるが,実際に必要な型は

WellTypedTerm (tx :: substCxt σ (liftCxt (count (Lam s)) Γ)) t

Page 41: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda

lambda

41

wの型は展開すると

WellTypedTerm (tx :: substCxt σ (liftCxt (count s) (liftCxt 1 Γ))) t

であるが,実際に必要な型は

WellTypedTerm (tx :: substCxt σ (liftCxt (count (Lam s)) Γ)) t

   「環境部分」が異なっている

Page 42: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda

lambda

42

この2つを eq という証明を用いて結んでいる

Page 43: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda

application

43

well-scoped の時と同様,

1. App s1 s2 において,s1 の型推論によって返された代入 σ1 を Γ に加え,

2. 環境 σ1 Γ の元 での s2 の型推論が成功した場合にのみ,

3. (s2 の型⇒ β)が s1 と等しくなっているかどうかを unify 関数によって判定する

Page 44: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda

application

44

Page 45: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda45

• 再帰呼び出しの結果を w1, w2 に受け取っていることと最後に well- typed な AppW1W2 を返している点のみが異なっている

•AppW1W2 はほぼ App w1 w2 となる

•しかし lambda と同様に型の調整を行う必要がある

Page 46: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda

application

46

WellTypedTerm (substCxt σ 3 (liftCxt 1 (substCxt σ 2 (liftCxt (count s2) (substCxt σ 1 (liftCxt (count s1) Γ)))))) (substType σ 3 (liftType 1 t2))

substWTerm σ 3 (liftWTerm 1 w2) の型は

となる.

Page 47: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda

application

47

WellTypedTerm (substCxt σ 3 (liftCxt 1 (substCxt σ 2 (liftCxt (count s2) (substCxt σ 1 (liftCxt (count s1) Γ)))))) (substType σ 3 (liftType 1 t2))

substWTerm σ 3 (liftWTerm 1 w2) の型は

となる.これと w2’ を見比べると

w2’ : WellTypedTerm (substCxt (σ 3 +!+ (σ 2’ +!+ σ 1’)) (liftCxt (count (App s1 s2)) Γ)) (substType σ 3 (liftType 1 t2))

Page 48: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda

application

48

WellTypedTerm (substCxt σ 3 (liftCxt 1 (substCxt σ 2 (liftCxt (count s2) (substCxt σ 1 (liftCxt (count s1) Γ)))))) (substType σ 3 (liftType 1 t2))

得られた w2 の型は

欲しい w2 の型は

w2’ : WellTypedTerm (substCxt (σ 3 +!+ (σ 2’ +!+ σ 1’)) (liftCxt (count (App s1 s2)) Γ)) (substType σ 3 (liftType 1 t2))

Page 49: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda

すなわち1. (count s1) だけ型変数を増やし,σ1 を施す

2. (count s2) だけ型変数を増やし,σ2を施す

3. 1 だけ型変数を増やし,σ3を施す

49

1. 一度に (count (App s1 s2)) だけ型変数を増やし,(σ1 + (σ2 + σ3 )) を施す

を示す必要がある.

Page 50: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda

すなわち1. (count s1) だけ型変数を増やし,σ1 を施す

2. (count s2) だけ型変数を増やし,σ2を施す

3. 1 だけ型変数を増やし,σ3を施す

50

1. 一度に (count (App s1 s2)) だけ型変数を増やし,(σ1 + (σ2 + σ3 )) を施す

この証明はかなり長くなるが示すことができる

Page 51: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda

application

51

w1’ : WellTypedTerm (substCxt (σ 3 +!+ (σ 2’ +!+ σ 1’)) (liftCxt (count (App s1 s2)) Γ)) (substType σ 3 (liftType 1 t2 ⇒ TVar (fromN m2)))

w1 : WellTypedTerm (substCxt σ 1 (liftCxt (count s1) Γ)) t1

w1に関して,型は

w1’ の型は

Page 52: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda52

w1’ : WellTypedTerm (substCxt (σ 3 +!+ (σ 2’ +!+ σ 1’)) (liftCxt (count (App s1 s2)) Γ)) (substType σ 3 (liftType 1 t2 ⇒ TVar (fromN m2)))

WellTypedTerm (substCxt σ 3 (liftCxt 1 (substCxt σ 2 (liftCxt (count s2) (substCxt σ 1 (liftCxt (count s1) Γ)))))) (substType σ 3 (liftType 1 (substType σ 2 (liftType (count s2) t1)))

w1 に対して一連の操作を行って得られた substWTerm σ 3 (liftWTerm 1 (substWTerm σ 2 (liftWTerm (count s2) w1)))

の型は

対して,欲しい型は

Page 53: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda

application• w2 の場合と異なり,環境も型も異なる → 環境に関しては w2 と同じ方法で示せる

• 型に関して

53

substType σ 3 (liftType 1 (substType σ 2 (liftType (count s2) t1)))

substType σ 3 (liftType 1 t2 ⇒ TVar (fromN m2))

が同じであることを示す必要がある

Page 54: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda

application

• 両者は unify 関数によって同じ型になっているはず

• これまでの unify 関数はそれを保証していない

• 2つの型が同値であることの証明がついた unify 関数が必要

54

substType σ3 (liftType 1 (substType σ2 (liftType (count s2) t1)))

substType σ3 (liftType 1 t2 ⇒ TVar (fromN m2))

Page 55: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda

application

• 両者は unify 関数によって同じ型になっているはず

• これまでの unify 関数はそれを保証していない

• 2つの型が同値であることの証明がついた unify 関数が必要

55

σ3 (σ2 (t1))

σ3 (t2 ⇒ β)

Page 56: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda

application

• 証明付きの unify を実装する必要がある→ unify2 関数とする

• その証明を用いて w2 と w2’ を結ぶ

• unify2 関数に関しては次に説明

56

Page 57: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda

証明付きの unify• well-scoped な項の場合だと unify は「何か代入を返すもの」と定義されているのみで「返って来た代入が確かにふたつの型を単一化するか」は保証されていない

• well-typed な型推論器を実装するにおいて,ふたつの型が確かに等しいことを保証するような unification の関数が必要

• それにあたり,flexFlex,flexRigid なども変換前後における型の代入が等しいことの証明を添えて返すように変更する必要がある

• 新しい flexFlex2,flexRigid2 を定義する

57

Page 58: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda

証明付きの unify

58

flexFlex2, flexRigid2 : σ が確かに与えられた 2 つの型変数,もしくは型を単一化するという証明を加えて返す

Page 59: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda

証明付きの unify

59

これを利用して,amgu2 も

 「substType したものが等しい」という証明に加えて, さらに amgu2 を定義する際の帰納法の仮定に必要となる

「返って来た σ が acc の拡張になっている」

という証明を加える

Page 60: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda

証明付きの unify

60

unify2 t1 t2 :

型変数が m 個であるような型 t1 と t2 を単一化する代入と,その二つの substitution した型が等しい証明を返す

Page 61: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda

証明付きの unify

61

well-typed な型推論器の実装においては well-scoped の場合と同じく型変数を巧妙に操作することに加えて,「 unify の拡張, flexFlex や flexRigid といった機構の定式化」も必要となった

Page 62: Agda による型推論器の定式化

http://github.com/KDXU/InferAgda

まとめ• Agda で simply-typed lambda calculus の unification の機構を McBride の手法を用い型推論器の実装を行った

• さらに型推論器が正しいことを示すために正当性の証明がついた型推論器を作成した

• 今後は syntax の拡張として多相の Let 文を含めた形についても実装と証明を行っていきたい

• 現在の実装だと where 節が冗長なため実行時間が比較的長くなってしまっており,かつ証明が煩雑なので実装の簡略化を試みていきたい

62