14. E1x = E2x
E1 = E2
(E-EXT2)
実際にこの 2 つの体系が同等であるかは証明する必要があるでしょう. ここでは Coq という定理
証明支援器を用いて証明を行ってみることにしましょう. もちろん紙上での証明も可能です. ここで
は (E-EXT2) ⇒ (E-EXT1) と (E-EXT1) ⇒ (E-EXT2) の 2 つに分けて証明を行いましょう. そ
れぞれ λ+(E-EXT2) が (E-EXT1) を満たすこと, また λ+(E-EXT1) が (E-EXT2) を満たすこと
を証明します. Coq での定義では型付けが行われていますが, 本質的に同じものです.
(E-EXT2) ⇒ (E-EXT1) の証明
(* 推論規則 (E-EXT2) を公理として導入 *)
Axiom ext2 : forall X Y: Type f g : X - Y,
(forall (x: X), f x = g x) - f = g.
(* (E-EXT1) を満たすことを証明 *)
Theorem ext1 : forall X Y:Type f : X - Y,
forall (x:X), (fun x = f x) = f.
Proof.
intros.
apply ext2.
reflexivity.
Qed.
13
15. (E-EXT1) ⇒ (E-EXT2) の証明
(* 推論規則 (E-EXT1) を公理として導入 *)
Axiom ext1 : forall X Y:Type,
forall (f : X - Y) (x:X), (fun x = f x) = f.
(* 推論規則 (E-ABS) をλ式を関数適用の形に限定した規則を導入 *)
Axiom abs : forall X Y : Type,
forall (f g : X - Y) (x:X),
(f x = g x) - (fun x = f x) = (fun x = g x).
(* (E-EXT2) を満たすことを証明 *)
Theorem ext2 : forall X Y: Type f g : X - Y,
forall (x: X), (f x = g x) - f = g.
Proof.
intros.
assert (f_eta := ext1 f x).
assert (g_eta := ext1 g x).
rewrite - f_ext1.
rewrite - g_ext1.
assert (fg_abs := abs f g x).
assert (abs_eq := fg_abs H).
rewrite - abs_eq.
reflexivity.
Qed.
この 2 つの体系が同等であることがわかったので次からはこの拡張されたラムダ計算の体系は
λ+(E-EXT) と統一して書くことにします. そしてこの λ+(E-EXT) は外延的ラムダ計算と呼ばれま
す. (E-EXT2) は前章までのラムダ計算の体系 λ の推論規則 (E-ABS) の自然な拡張をした推論規則
であることを紹介してこの章を終わりにします. この推論規則において E1 = (λx.P)x, E2 = (λx.Q)x
を考えると,
(λx.P)x = (λx.Q)
λx.P = λx.Q
となります. これは λ ⊢ (λx.E)x = E となるからです. これは (E-EXT2) の λ 式 E1, E2 を λ 抽象
の形式 λx. · · · のみに限定したものであることが分かります.
14
16. 第4章 不動点コンビネータ
第 3 章で自由変数を含まない λ 式をコンビネータと呼ぶと説明しました. 本章では重要なコンビ
ネータの 1 つとして, 不動点コンビネータを紹介することにします. 不動点コンビネータは再帰関数
を定義するために用いることが出来ます.
4.1 不動点と不動点コンビネータ
少しラムダ計算から離れて一般的な関数についてを考えることにします. 最初に次のような仮引数
に関数を受け取り, 関数を返す高階関数 F を考えてみましょう. その関数が次のような関係を満たし
ていたとします.
F(f) = f
このとき上記のような関係を満たすような f を F の不動点と言います. ラムダ計算においては任意
の λ 式について不動点が存在します.
ここで 1 から n ( 0) までの自然数の合計の和を取る関数を考えましょう. 次のような再帰関数で
そのような和を計算することが出来るでしょう.1
sum(x) = if x = 0 then 0 else x + sum(x − 1)
つぎのような高階関数 F′
も仮引数に sum を適用すると同じように和を取ることが出来ることが分
かります.
F′
(f)(x) = if x = 0 then 0 else x + f(x − 1)
つまり sum は F′
の不動点であると言えます.
つぎに不動点コンビネータを紹介します. 不動点コンビネータ g は任意の関数 f に対して
f(g(f)) = g(f)
を満たすものを言います.
ここで不動点コンビネータが実際にどのように使えるかということを簡単に見るために Haskell の
プログラムを例にして, 見ていくことにします.
{- 不動点コンビネータ -}
fpc f = f (fpc f)
sum n f 0 = 0
sum n f x = x + f (x − 1)
1ここで if then else のような構文がラムダ計算の枠組みでどのように表されるか気になった方は第 6 章までしばしお待
ちを.
15
17. ここでは先ほど例で出した関数 sum を sum n として定義しました. これを実際に和を計算させる
には, 次のように不動点コンビネータ fpc を用いて呼び出せば良いことが分かります.
? fpc sum n 3
6
? fpc sum n 10
55
OCaml の場合には値呼び出しにより引数から先に評価されてしまい, つぎのような
let rec fpc f = f (fpc f)
の定義では, 実行時に fpc の呼び出しが無限に行われてしまいます. そのため, 次のように定義した
りすることで呼び出しが無限に行われないようにすることが出来ます.
let rec fpc f x = f (fpc f) x
もしくは次のように遅延計算をさせる lazy を導入することで次のように定義することでも出来ます.
let rec fpc f = f (lazy (fpc f))
let rec sum n f x =
if x = 0 then 0
else x + Lazy.force(f)(x − 1)
ここで次の定理を示しておきましょう.
定理 4.1.1
任意の λ 式 P に対して Q = λx1 · · · xn.P[q/Q] を満たす P が存在する.
証明
Q ≡ Y(λpx1 · · · xn.P)
とおくと, 不動点コンビネータの性質から
Q ≡ (λpx1 · · · xn.P)Q
であり, Q = λx1 · · · xn.P[q/Q] が得られる. □
4.2 様々な不動点コンビネータ
型付けの無いラムダ計算における不動点コンビネータは無数にあり, 全てを挙げることに意味はあ
りません. しかし有名なものをここではいくつか例示しておくことにします.
4.2.1 Y コンビネータ
ここでまたラムダ計算に戻って不動点コンビネータを考えていくことにします. 型なしラムダ計算
において有名な不動点コンビネータとして Y コンビネータを見てみましょう.
定義 4.2.1 Y コンビネータの定義
Y ≡ λf.(λx.f(xx))(λx.f(xx))
□
16
18. これが不動点コンビネータになっていることを証明するのは簡単です.
YE = (λx.E(xx))(λx.E(xx))
= E((λx.E(xx))(λx.E(xx)))
= E(YE)
ここで気がつくのは Y コンビネータでは, xx のように自分自身に関数適用することになっています.
このような定義が可能なのは型付けがないことに起因しています2
.
4.2.2 Z コンビネータ
Z コンビネータは値呼び出しのプログラミング言語でも利用できる不動点コンビネータです. これ
は Y コンビネータの部分式を変換した次のようなものになります.
定義 4.2.2 Z コンビネータの定義
Z ≡ λf.(λx.f(λy.(xx)y))(λx.f(λy.(xx)y))
□
ここでは OCaml で Z コンビネータを定義してみましょう. しかし素朴に定義 4.2.2 から定義し
ようとしてもおそらく型が解決が出来ずにエラーとなるでしょう. OCaml で Z コンビネータを定
義する際には, ”equi-recursive type”と呼ばれる型を導入することで解決できます3
. ”equi-recursive
type”の詳細は置いておくとして, OCaml で”equi-recursive type”を用いる場合は -rectypes という
オプションを付けると用いることが出来ます. -rectypes オプションを付けた上で次のコードを定義
してやれば, 定義できるでしょう.
let z = fun f → (fun x → (fun y → f (x x) y)) (fun x → (fun y → f (x x) y))
4.2.3 Θ コンビネータ
チューリング不動点コンビネータと呼ばれる不動点コンビネータです.
定義 4.2.3 チューリング不動点コンビネータ Θ の定義
Θ ≡ (λxy.(y(xx)y))(λxy.y((xx)y))
□
2ここでは Y コンビネータをプログラム上で定義しませんでした. これは Y コンビネータを実際のプログラム上で定義す
るには工夫が必要となることが多いためです. 特に型付けのある言語では Y コンビネータの型付けは一般にはできません. し
かし”equi-recursive type”と呼ばれるような型つけが利用できる言語では Y コンビネータの型付けができ, 定義できる場合
があります. 多相性を持つ場合は 4.1 節での Haskell のコードのようにすることで不動点コンビネータを直接定義できます.
3OCaml の”equi-recursive type”は非常に面白く, 例えば let x = (1, x) のような定義も可能になります. これは”equi-
recursive type”では type t = t → t のように型の定義が再帰をしていても両辺を等価だと見なすことができ, 定義ができ
るということに起因しています.
17
26. (1) 加法関数
(5)-(b) の定義において
{
g(x) = x
h(x1, x2, x3) = Succ x
とすることで関数 f は足し算を行う関数として定義できることが分かります. 例えば 2+3 に関
しては
f(2, 3) = h(f(1, 3), 1, 3) = Succ (f(1, 3))
= Succ (h(f(0, 3), 0, 3))
= Succ (Succ 3)
= Succ 4
= 5
のようになります.
ここでいくつかの足し算における性質を満たしているかを証明しておくことにしましょう.
原始帰納的関数として定義した加法関数の性質の証明
Definition g (x : nat) := x.
Definition h (x y z : nat) := S x.
Fixpoint f (x y : nat) :=
match x with
| 0 = g y
| S z = h (f z y) z y
end.
(* f, g, h で unfold するためのタクティク *)
Tactic Notation unfold_fgh :=
unfold f; unfold g; unfold h.
Tactic Notation unfold_fgh_in constr(E) :=
unfold f in E; unfold g in E; unfold h in E.
(* (Succ x) + y = x + (Succ y) *)
Theorem replacement_succ: forall (x y : nat), f (S x) y = f x (S y).
Proof.
intros.
induction x.
unfold_fgh.
reflexivity.
unfold_fgh. unfold_fgh_in IHx.
f_equal. apply IHx.
Qed.
25
27. (* Succ(x + y) = (Succ x) + y *)
Theorem succ_f_is_f_succ: forall (x y : nat), S (f x y) = f (S x) y.
Proof.
intros.
unfold_fgh.
f_equal.
Qed.
(* Succ(x + y) = x + (Succ y) *)
Theorem succ_f_is_f_succ_rep: forall (x y : nat), S (f x y) = f x (S y).
Proof.
intros.
assert (rep_succ := replacement_succ x y).
rewrite - rep_succ.
assert (succ_f := succ_f_is_f_succ x y).
apply succ_f.
Qed.
(* x + y = y + x *)
Theorem comutation_law: forall (x y : nat), f x y = f y x.
Proof.
intros.
induction x. induction y.
reflexivity.
unfold_fgh. unfold_fgh_in IHy.
rewrite - IHy.
reflexivity.
assert (succ_f := succ_f_is_f_succ x y).
assert (succ_f_rep := succ_f_is_f_succ_rep y x).
rewrite - succ_f. rewrite - succ_f_rep.
f_equal.
apply IHx.
Qed.
この他にも x + y = x + z ならば y = z, (x + y) + z = x + (y + z) なども証明してみると良い
でしょう.
26