型付きλ計算とHaskell
MenuHaskell を動かしながら、型付きλ計算を理解しよう。
項の構築
primitive data type がいくつかあり、それは項(term)である。
1
'a'
次に、変数を導入する。変数は項である。 変数には primitive data が入る。例えば整数とか、例えば文字列とか。あるいは、任意の項とか。
x = 'a'
(Haskell では let x = 'a' を使う)
次にλ式を導入する。変数 x を含む(あるいは含まない)項 R x があった時に、 \ x -> R x は項である。項なので変数に代入することができる。
id = \ x -> x
\x -> R x は入力を一つ受け取る関数である。 関数を作る方法はここまでではこれしかない。
ある項からλ項を作るのは、項の変換になる。これを以下のように横線を使って書く。この作り方を→Iと名付ける。I は introduction である。
R x
------------------ →I
\ x -> R x
関数fを呼び出すには、関数の項に後ろに引数となる項xを書く。
f x
ここまでで、項は、primitive data type と変数とλ式しかない。これらを組み合わせたものが項になる。
α変換
λ式 \ x -> R x に引数 'a' を渡すと、後ろの項の x を、その値で置き換えた R 'a' を返す。これをα変換という。あるいは、λ式の関数適用である。これが、λ式の意味だと思っても良い。(操作的意味)これは二つの項から一つの項を作る、つまり、項を減らす操作になる。これを以下のように書く。この作り方を→Eと名付ける。E は Elimination である。
( \ x -> R x ) y
--------------------------- -→E
R y
ここで R x は変数xを含む、あるいは含まない任意の項である。あるいは、その項を表すメタ変数である。
id '1'
を Haskell で実行してみよう。また、
( \ x -> x ) '1'を実行してみよう。
型の構築
型は、λ計算の整合性を保証するために必要となる。まず、primive な data 型がある。ある値が型を持つことを :: で表す。
'a' :: Char
二つの型 a b があった時に、a を入力として b を返す関数の型を、
a -> b
と書く。二つの入力 a と b があって、型 c を返す場合は、
a -> b -> c
と書く。
a -> ( b -> c )
これは、a を受けとって、b -> c という関数を返す関数である。
( a -> b ) -> c
これは、関数を受けとって、c という型を返す関数である。この関数の入力は一つである。
型 a を持つ項 x を含む型 b を持つ項 R x : b に対して、λ式を作ると、a -> b が作られる。
b
----------------------------------- →I
a -> b
a -> b は関数で、これに型 a の値を引数として与えると、
a -> b a
----------------------------------- →E
b
となる。これは→の導入と削除になっている。
型と項は同時に操作される
この操作は、型と値と両方同時に起きている。つまり、
x : a と r x : b
から、
\ x -> r x : a -> b
が作られる。あるいは、
\ x -> r x : a -> b と y : a
から、
r y : b
が作られる。
id : a -> a という型を持つ項を作りたいとする。その項をλ項として構築するのを以下のように記述する。
id : a -> a
id = \x -> x
これは Haskell のプログラムそのものである。
論理式としての型
型 a を命題変数だと思う。
a -> b
は「a ならば b 」である。
b が成立していれば、
a -> b
が常に正しい。つまり、
b
----------------------------------- →I
a -> b
また、a -> b が常に正しくて、a が正しければ b が成立する。
a -> b a
----------------------------------- →E
b
ここまでの範囲で、λ計算の型と論理式のとが対応していることがわかる。この変換はλ項の構築に対応しているので、論理式の証明がλ項の対応することになる。
論理式 ⇔ 型 証明 ⇔ λ項
しかし、∧とかが出てこない。この対応を維持したまた、論理を拡張できるのかというのが問題になる。
直積
∧ は型を二つとる二項演算子である。a と b が型なら、 a ∧ b も型になる。
a b
----------------------------------- ∧I
a ∧ b
もし、a ∧ b が正しければ、 a は正しい
a ∧ b
----------------------------------- ∧E1
a
b の方もある。
a ∧ b
----------------------------------- ∧E1
b
この∧I, ∧E1, ∧E1 に相当するλ項を新しく導入したい
p1 : a, p2 : b があった時に、
p1 × p2 : a ∧ b
となって欲しい。そして、p : a ∧ b があった時に、
π1 p : a
π2 p : b
となって欲しい。× と π1 と π2 が新しく導入する項になる。この項に対応する操作を定義する。
p1 : a p2 : b
----------------------------------- ∧I
p1 × p2 : a ∧ b
p : a ∧ b
----------------------------------- ∧E1
π1 p : a
p : a ∧ b
----------------------------------- ∧E2
π2 p : b
これらは Haskell では以下のような構文を使う
data Product a b = P { x :: a , y :: b }
let p = P 'a' 1
x p
y p
それぞれの型を確認してみよう。
直積を使った証明
Sum 排他的和
∧ はできたが、∨ はどうしよう? ∧の上下をひっくり返すと良さそうに思える。
a
----------------------------------- ∨E1
a ∨ b
b
----------------------------------- ∨E2
a ∨ b
こっちは良さそう。
a ∨ b
-----------------------------------
a b
これはよろしくない。下は一つでないと。
a ∨ b
-----------------------------------
:
:
-----------------------------------
c
となるのが望ましい。
a ∨ b a -> c b -> c
-------------------------------------- ∨I
c
というのが正しそう。この∨は、命題論理の∨と少し違う。
これに対応する項は、
x : a
----------------------------------- ∨E1
L x : a ∨ b
y : b
----------------------------------- ∨E1
R y : a ∨ b
s : a ∨ b i1 : a -> c i2 : b -> c
-------------------------------------------------------------- ∨I
?