web-dev-qa-db-ja.com

カリーハワード同形性

インターネットで調べてみたところ、論理理論の講義にどんどん退化していない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 "の値を作成することができます。また、関数本体は、これをどのように行うかを正確に記述しています。

それは理にかなっているようですが、それほどではありません興味深い。だから明らかにこれ以上のものがあるに違いない...

54

カリーハワード同形性は、型が命題に対応し、値が証明に対応することを単に述べています。

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の値がある場合、abと同じタイプです。Equal a bは命題a=b。問題は、この方法ではtypesの同等性についてしか話せないことです。しかし、 依存型 の場合、この定義をany値で機能するように簡単に一般化できるため、Equal a bは命題に対応します。 abが同一であること。したがって、たとえば、次のように書くことができます。

type IsBijection (f :: a -> b) (g :: b -> a) =
    forall x. Equal (f (g x)) (g (f x))

ここで、fgは通常の関数なので、fは簡単にInt -> Intと入力します。繰り返しますが、Haskellはこれを行うことができません。このようなことを行うには、依存型が必要です。

典型的な関数型言語は、依存型がないだけでなく、すべてのaに対して型aを持ち、命題の証明として機能する⊥のために、証明を書くのにあまり適していません。しかし totalCoqAgda のような言語は、対応を利用して証明システムとしても機能します依存型のプログラミング言語として。

44
ehird

おそらく、それが何であるかを理解するための最良の方法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の戦術のようなものです)。

他に証明できること:

  • ベクトルの場合はhead ∘ init ≡ headここ

  • コンパイラは、Coqの同じ(ホスト)プログラムの解釈で取得された値と同じ値を実行するプログラムを生成します こここの本 は、言語モデリングとプログラム検証のトピックについてもよく読んでいます。

  • マルティン・レーフの表現力の型理論はZFCと同等であるため、構成主義数学で証明できるものは他にあります。実際、カリーハワード同形性は 物理学とトポロジー および 代数的トポロジー に拡張できます。

2
JJJ

私が思いつくことができる唯一の賢明な答えはこれです:関数X-> Y-> Zがある場合、型シグネチャは「型Xの値と、型Yの値を作成できると仮定すると、タイプZ "の値を作成することができます。また、関数本体は、これをどのように行うかを正確に記述しています。それは理にかなっているようですが、あまり面白くありません。だから明らかにこれ以上のものがあるに違いない...

ええ、そうです、それは多くの意味を持ち、多くの質問を開くので、かなり多くのことがあります。

まず第一に、CHIについてのあなたの議論は、含意/関数タイプ(_->_)の観点からのみ組み立てられています。これについては話しませんが、接続詞と論理和がそれぞれ積型と和型にどのように対応するかを見たことがあるでしょう。しかし、否定、全称記号、存在記号などの他の論理演算子についてはどうでしょうか。これらを含む論理的証明をプログラムにどのように変換しますか?おおまかに次のようになります。

  • 否定はファーストクラスの継続に対応します。これを説明するように私に頼まないでください。
  • 命題(個々ではない)変数の全称記号は、パラメトリック多相に対応します。したがって、たとえば、ポリモーフィック関数idのタイプは実際には_forall a. a -> a_です。
  • 命題変数の存在記号は、データまたは実装の非表示に関係するいくつかのことに対応します。抽象データ型モジュールシステムおよび動的ディスパッチ =。 GHCの実存的なタイプはこれに関連しています。
  • 個々の変数に対する全称記号と存在記号は、依存型システムにつながります。

それ以外に、論理に関するあらゆる種類の証明がプログラミング言語に関する証明に即座に変換されることも意味します。たとえば、直観主義的命題論理の決定可能性は、単純型付きラムダ計算ですべてのプログラムを終了することを意味します。

さて、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)

これらの規則は、自然数の加算に関する多くの定理を証明するのに十分です。これらの証明もプログラムになります。

2
Luis Casillas