この参照から:厳密な陽性
厳密な陽性条件は、次のような宣言を除外します
data Bad : Set where
bad : (Bad → Bad) → Bad
A B C
-- A is in a negative position, B and C are OK
なぜAは負ですか?また、なぜBが許可されるのですか?Cが許可される理由を理解しています。
この参照から:厳密な陽性
厳密な陽性条件は、次のような宣言を除外します
data Bad : Set where
bad : (Bad → Bad) → Bad
A B C
-- A is in a negative position, B and C are OK
なぜAは負ですか?また、なぜBが許可されるのですか?Cが許可される理由を理解しています。
回答:
次に、帰納的データ型について説明します。
代数では、操作は有限数の引数を取ることが一般的であり、ほとんどの場合、操作はゼロ(定数)、1(単項)、または2(バイナリ)の引数を取ります。データ型のコンストラクタに対してこれを一般化すると便利です。仮定c
データ型のコンストラクタですT
。
c
一定である我々は関数として考えることができunit -> T
、または同等に(empty -> T) -> T
、c
単項である場合、それを関数T -> T
、または同等(unit -> T) -> T
に考えることができます、c
バイナリの場合、それを関数T -> T -> T
、または同等にT * T -> T
、または同等(bool -> T) -> T
に考えることができます。c
7つの引数をとるコンストラクタが必要な(seven -> T) -> T
場合seven
は、7つの要素を持つ以前に定義された型である関数と見なすことができます。c
無限に多くの引数を取るコンストラクターを持つこともできます(nat -> T) -> T
。それは関数です。これらの例は、コンストラクターの一般的な形式が
c : (A -> T) -> T
我々は呼んでどこアリティのを、私たちは考える取るコンストラクタとしてタイプの-many引数をの要素を生成します。A
c
c
A
T
T
これは非常に重要なものです:アリティはを定義する前に定義する必要があります。T
そうしないと、コンストラクターが何をすることになっているのかわかりません。誰かがコンストラクターを作ろうとした場合
broken: (T -> T) -> T
次に、「引数はいくつbroken
必要か」という質問です。良い答えはありません。あなたはそれを「T
多くの引数をとる」で答えようとするかもしれませんが、T
まだ定義されていないので、それはしません。ファンシーな固定小数点理論を使用して型T
と単射関数を見つけることで、問題から抜け出そうとするかもしれませんが、成功するかもしれませんが(T -> T) -> T
、誘導原理も途中で破っT
てしまいます。だから、そのようなことを試すのは悪い考えです。
c
B
c : B * (A -> T) -> T
実際、多くのコンストラクタは、このように書き換えることができ、すべてではありませんが、我々はすなわち、私たちができるようにする必要があり、一歩を必要とA
する依存にB
:
c : (∑ (x : B), A x -> T) -> T
これは帰納型のコンストラクターの最後の形式です。それはまた、正確にはWタイプです。フォームは非常に一般的であるため、必要なコンストラクタは1つだけc
です。確かに、2つある場合
d' : (∑ (x : B'), A' x -> T) -> T
d'' : (∑ (x : B''), A'' x -> T) -> T
その後、それらを1つに組み合わせることができます
d : (∑ (x : B), A x -> T) -> T
どこ
B := B' + B''
A(inl x) := A' x
A(inr x) := A'' x
ちなみに、一般的な形をカレー化すると、
c : ∏ (x : B), ((A x -> T) -> T)
これは、人々が実際に証明アシスタントに書き留めたものに近いものです。証明アシスタントを使用すると、便利な方法でコンストラクターを書き留めることができますが、これらは上記の一般的な形式(演習!)と同等です。
の最初の出現はBad
、関数の引数を表すため、つまり関数の矢印の左側にあるため、「負」と呼ばれます(Philip Wadlerによる無料の再帰型を参照)。「ネガティブポジション」という用語の起源は、反変の概念に由来すると思います(「コントラ」は反対を意味します)。
型を負の位置に定義することはできません。なぜなら、それを使用して非終了プログラムを作成できるためです。つまり、その存在下で強力な正規化が失敗します(これについては以下で詳しく説明します)。ちなみに、これが「厳密な陽性」というルールの名前の理由です。負の位置は許可されません。
Bad
非終了を引き起こさずBad
、再帰データ型のある時点(コンストラクタの最後の矢印の前)で定義されている型()を使用するため、の2番目の出現を許可します。
以下の定義は厳密な陽性の規則に違反していないことを理解することが重要です。
data Good : Set where
good : Good → Good → Good
このルールは、コンストラクターの引数(Good
この場合は両方)にのみ適用され、コンストラクター自体には適用されません(Adam Chlipalaの「依存型の認定プログラミング」も参照してください)。
厳格な陽性に違反する別の例:
data Strange : Set where
strange : ((Bool → Strange) → (ℕ → Strange)) → Strange
^^ ^
this Strange is ...this arrow
to the left of...
また、ネガティブポジションについてこの回答を確認することもできます。
厳密に正でない宣言は、それを使用して非終了関数を記述できるため、拒否されます。上記のBadデータ型を使用してループ定義を作成する方法については、BadInHaskellを参照してください。
以下はOcamlの類似の例で、再帰を直接使用せずに(!)再帰的な動作を実装する方法を示しています。
type boxed_fun =
| Box of (boxed_fun -> boxed_fun)
(* (!) in Ocaml the 'let' construct does not permit recursion;
one have to use the 'let rec' construct to bring
the name of the function under definition into scope
*)
let nonTerminating (bf:boxed_fun) : boxed_fun =
match bf with
Box f -> f bf
let loop = nonTerminating (Box nonTerminating)
nonTerminating
機能「アンパック」オリジナル引数の引数とりんごそれから機能。ここで重要なことは、ほとんどの型システムは関数をそれ自体に渡すことを許可しないため、f f
タイプf
チェッカーを満たすための型がないため、などの用語は型チェックを行わないということです。タイプシステムが導入された理由の1つは、セルフアプリケーションを無効にすることです(ここを参照)。
上で紹介したようなデータ型のラッピングを使用して、不整合が生じる前にこの障害を回避できます。
終了しない計算が論理システムに不整合をもたらすことを付け加えたいと思います。AgdaとCoqの場合、False
帰納的データ型にはコンストラクターがないため、False型の証明項を構築できません。しかし、終了しない計算が許可されている場合、たとえばCoqで次のように実行できます。
Fixpoint loop (n : nat) : False = loop n
次に、タイプloop 0
チェックを与えることloop 0 : False
になるので、カリーハワードの対応の下では、誤った命題を証明したことになります。
効果:帰納的定義の厳密な陽性規則は、ロジックにとって悲惨な非終了計算を防ぎます。
A
。