インターネットで調べてみたところ、論理理論の講義にどんどん退化していないCHIの説明が頭に浮かびました。 (これらの人々は、「直観的命題論理」が実際に何かを意味する通常の人間にとってのフレーズであるかのように話します!)
大まかに言えば、CHIは、型は定理であり、プログラムはそれらの定理の証明であると言います。しかし、それでも平均 ??
これまでのところ、私はこれを理解しました:
id :: x -> x
を検討してください。そのタイプは、「Xが真であるとすれば、Xが真であると結論付けることができます」と言います。私には合理的な定理のように思えます。
ここで、foo :: x -> y
について考えてみましょう。 Haskellプログラマーなら誰でも言うように、これは不可能です。この関数を書くことはできません。 (まあ、とにかく不正行為をすることなく。)定理として読むと、「Xが真であるとすれば、Yは真であると結論付けることができます」と書かれています。これは明らかにナンセンスです。そして、確かに、この関数を書くことはできません。
より一般的には、関数の引数は「これが真であると見なされる」と見なすことができ、結果タイプは「他のすべてが真であると仮定して真である」と見なすことができます。 x -> y
のような関数の引数がある場合、Xが真であるということは、Yが真でなければならないことを意味すると仮定することができます。
たとえば、(.) :: (y -> z) -> (x -> y) -> x -> z
は、「YがZを意味し、XがYを意味し、Xが真であると仮定すると、Zが真であると結論付けることができます」と見なすことができます。これは私には論理的に理にかなっているようです。
さて、Int -> Int
はどういう意味ですか? o_O
私が思いつくことができる唯一の賢明な答えはこれです:関数X-> Y-> Zがある場合、型シグネチャは「型Xの値と、型Yの値を作成できると仮定すると、タイプZ "の値を作成することができます。また、関数本体は、これをどのように行うかを正確に記述しています。
それは理にかなっているようですが、それほどではありません興味深い。だから明らかにこれ以上のものがあるに違いない...
カリーハワード同形性は、型が命題に対応し、値が証明に対応することを単に述べています。
Int -> Int
は、論理的な命題としてはそれほど興味深い意味ではありません。何かを論理的な命題として解釈するときは、型が人が住んでいる(値がある)かどうかだけに関心があります。したがって、Int -> Int
は、「Int
が与えられた場合、Int
を提供できます」という意味であり、もちろんそれは真実です。それには多くの異なる証明がありますが(そのタイプのさまざまな異なる関数に対応します)、それを論理的な命題としてとらえるとき、あなたは本当に気にしません。
それは、興味深い命題がそのような機能を含むことができないという意味ではありません。命題として、その特定のタイプは非常に退屈です。
完全にポリモーフィックではなく、論理的な意味を持つ関数型のインスタンスについては、p -> Void
(一部のp)を検討してください。ここで、Void
は無人型です。値のない型(Haskellの⊥を除くが、後で説明します)。タイプVoid
の値を取得する唯一の方法は、矛盾を証明できる場合(もちろん、不可能です)です。また、Void
は矛盾を証明したことを意味するため、そこから任意の値を取得できます(つまり、関数absurd :: Void -> a
)。したがって、p -> Void
は¬pに対応します。これは「pは虚偽を意味する」という意味です。
直観主義論理 は、一般的な関数型言語が対応する特定の種類の論理です。重要なのは、それが建設的です:基本的に、a -> b
の証明は、b
からa
を計算するためのアルゴリズムを与えます。通常の古典論理では真ではありません( 排中律 のため、何かが真または偽のいずれかであることがわかりますが、why)。
Int -> Int
のような関数は命題ほど意味がありませんが、他の命題でそれらについてステートメントを作成できます。たとえば、2つの型の同等性の型を宣言できます( [〜#〜] gadt [〜#〜] を使用):
data Equal a b where
Refl :: Equal a a
タイプEqual a b
の値がある場合、a
はb
と同じタイプです。Equal a b
は命題a=b。問題は、この方法ではtypesの同等性についてしか話せないことです。しかし、 依存型 の場合、この定義をany値で機能するように簡単に一般化できるため、Equal a b
は命題に対応します。 値aとbが同一であること。したがって、たとえば、次のように書くことができます。
type IsBijection (f :: a -> b) (g :: b -> a) =
forall x. Equal (f (g x)) (g (f x))
ここで、fとgは通常の関数なので、fは簡単にInt -> Int
と入力します。繰り返しますが、Haskellはこれを行うことができません。このようなことを行うには、依存型が必要です。
典型的な関数型言語は、依存型がないだけでなく、すべてのa
に対して型a
を持ち、命題の証明として機能する⊥のために、証明を書くのにあまり適していません。しかし totalCoq や Agda のような言語は、対応を利用して証明システムとしても機能します依存型のプログラミング言語として。
おそらく、それが何であるかを理解するための最良の方法meanは、命題およびプログラムとしてusingタイプを開始(または試行)することです。証拠として。 Agda (Haskellで書かれており、Haskellに似ています)のような依存型の言語を学ぶことをお勧めします。その言語にはさまざまな 記事とコース があります。 Agdaを学ぶ は不完全ですが、LYAHFGGの本のように、物事を単純化しようとしています。
簡単な証明の例を次に示します。
{-# OPTIONS --without-K #-} -- we are consistent
module Equality where
-- Peano arithmetic.
--
-- ℕ-formation: ℕ is set.
--
-- ℕ-introduction: o ∈ ℕ,
-- a ∈ ℕ | (1 + a) ∈ ℕ.
--
data ℕ : Set where
o : ℕ
1+ : ℕ → ℕ
-- Axiom for _+_.
--
-- Form of ℕ-elimination.
--
infixl 6 _+_
_+_ : ℕ → ℕ → ℕ
o + m = m
1+ n + m = 1+ (n + m)
-- The identity type for ℕ.
--
infix 4 _≡_
data _≡_ (m : ℕ) : ℕ → Set where
refl : m ≡ m
-- Usefull property.
--
cong : {m n : ℕ} → m ≡ n → 1+ m ≡ 1+ n
cong refl = refl
-- Proof _of_ mathematical induction:
--
-- P 0, ∀ x. P x → P (1 + x) | ∀ x. P x.
--
ind : (P : ℕ → Set) → P o → (∀ n → P n → P (1+ n)) → ∀ n → P n
ind P P₀ _ o = P₀
ind P P₀ next (1+ n) = next n (ind P P₀ next n)
-- Associativity of addition using mathematical induction.
--
+-associative : (m n p : ℕ) → (m + n) + p ≡ m + (n + p)
+-associative m n p = ind P P₀ is m
where
P : ℕ → Set
P i = (i + n) + p ≡ i + (n + p)
P₀ : P o
P₀ = refl
is : ∀ i → P i → P (1+ i)
is i Pi = cong Pi
-- Associativity of addition using (dependent) pattern matching.
--
+-associative′ : (m n p : ℕ) → (m + n) + p ≡ m + (n + p)
+-associative′ o _ _ = refl
+-associative′ (1+ m) n p = cong (+-associative′ m n p)
そこには、型としての(m + n) + p ≡ m + (n + p)
命題と、関数としてのその証明があります。このような証明には、より高度な手法があります(たとえば、 プレオーダー推論 、AgdaのコンビネータはCoqの戦術のようなものです)。
他に証明できること:
私が思いつくことができる唯一の賢明な答えはこれです:関数X-> Y-> Zがある場合、型シグネチャは「型Xの値と、型Yの値を作成できると仮定すると、タイプZ "の値を作成することができます。また、関数本体は、これをどのように行うかを正確に記述しています。それは理にかなっているようですが、あまり面白くありません。だから明らかにこれ以上のものがあるに違いない...
ええ、そうです、それは多くの意味を持ち、多くの質問を開くので、かなり多くのことがあります。
まず第一に、CHIについてのあなたの議論は、含意/関数タイプ(_->
_)の観点からのみ組み立てられています。これについては話しませんが、接続詞と論理和がそれぞれ積型と和型にどのように対応するかを見たことがあるでしょう。しかし、否定、全称記号、存在記号などの他の論理演算子についてはどうでしょうか。これらを含む論理的証明をプログラムにどのように変換しますか?おおまかに次のようになります。
id
のタイプは実際には_forall a. a -> a
_です。それ以外に、論理に関するあらゆる種類の証明がプログラミング言語に関する証明に即座に変換されることも意味します。たとえば、直観主義的命題論理の決定可能性は、単純型付きラムダ計算ですべてのプログラムを終了することを意味します。
さて、Int-> Intはどういう意味ですか? o_O
それはタイプ、あるいは命題です。 _f :: Int -> Int
_では、_(+1)
_は「プログラム」(関数と定数の両方を「プログラム」または代わりに証明として認める特定の意味で)に名前を付けます。言語のセマンティクスはf
を推論の原始的な規則として、またはf
がそのような規則と前提から構築できる証明である方法を示します。
これらのルールは、タイプの基本メンバーを定義する等式公理と、他のどのプログラムがそのタイプに存在するかを証明できるルールの観点から指定されることがよくあります。たとえば、Int
からNat
(0から先の自然数)に切り替えると、次のルールが適用されます。
0 :: Nat
_(_0
_はNat
の原始的な証明です)x :: Nat ==> Succ x :: Nat
_x :: Nat, y :: Nat ==> x + y :: Nat
_x + Zero :: Nat ==> x :: Nat
_Succ x + y ==> Succ (x + y)
これらの規則は、自然数の加算に関する多くの定理を証明するのに十分です。これらの証明もプログラムになります。