CoqExtraction/LetouzeyOverview/2
#2 Extraction in practice : div
In this section, we illustrate the use of Coq extraction on a small yet revealing example: Euclidean division amongst natural numbers. For sake of simplicity, we will use the unary nat datatype for representing these natural numbers: every number is stored as either zero or the successor S of another number. Even if this representation is inherently inefficient, the discussion that follows would be quite similar with more clever coding of numbers. Coq’s Standard Library provides basic operations and relations on nat, such as +, *, <, . In Coq, logical relations do not necessarily have corresponding boolean test functions, but here a result named le_lt_dec, noted afterwards, can be used as an effective comparison function for determining whether n m or m<n for any numbers n and m.
このセクションでは、私たちは、小さいが明らかにする例の上のCoq抽出の使用を説明します:自然数中のユークリッドの区分。単純性の目的については、私たちはこれらの自然数を表わすために単項のnatデータ・タイプを使用するでしょう:すべての数は0あるいは別の数の後継者Sのいずれかとして格納されます。この表現が本質的に非能率的でも、続く議論は数のより利口なコーディングで全く類似しているでしょう。Coqの標準ライブラリーは、+(*)のような natの上の基本操作および関係を提供します、<、 .Coqでは、論理比較は必ずしも対応するブールの検定関数を持っていません。しかし、ここで、結果はle_lt_decを指定し、注意しました。 後で、決定のために有効な比較機能として使用することができる、かどうか、nmあるいはm<任意の数nおよびmのためのn。
Each Coq snippet proposed below is taken verbatim from a valid session1 with Coq 8.2, including unicode notations and other syntactic improvements.
下に提案されたCoq断片はそれぞれ、unicode記法および他の構文的な改良を含むCoq 8.2を備えた、有効なsession1から逐語的に得られます。
##2.1 A division that fulfills the structural constraint
One usual algorithm for division on natural numbers is to proceed by successive subtractions: div x y = 0 when x<y and div x y = S (div (x-y) y) otherwise. But this cannot be written directly in Coq. Due to the intimate relationship between proofs and programs in Coq, no Coq objects may be allowed to trigger infinite computations. A rather drastic constraint is hence required on recursive functions in order to ensure their termination: they should have at least one inductive parameter such that recursive calls are done on an immediate subterm of this parameter. Here, our recursive call fails this criterion, since (x-y) is not an immediate subterm of x, and second parameter y has not changed. Even worse, trying this algorithm with y=0 leads to an infinite computation: Coq’s rejection is here quite legitimate.
自然数上の区分用の通常の1つのアルゴリズムは連続の減法によって進むことです:臆病者x y=0、いつ、x<yと臆病者のx y=S(臆病者(x-y)y)そうでなければ。しかし、これは、Coqの中で直接書くことができません。Coqの中の証拠およびプログラムの密接な関係により、Coqオブジェクトは、無限の計算を引き起こすことを許されないかもしれません。やや徹底的な制約は、従ってそれらの終了を保証するために帰納的関数上で必要です:再帰呼び出しがこのパラメーターの即時のサブ用語上で終っているように、それらには少なくとも1つの誘導のパラメーターがあるべきです。ここで、私たちの再帰呼び出しはこの基準に失敗します、以来(x-y)xの即時のサブ用語および第2のパラメーターyでない、変わっていません。さらにより悪く、y=0を備えたこのアルゴリズムの試みは無限の計算に結びつきます:Coqの拒絶はここに全く正当です。
For defining nonetheless our division in Coq, a first solution is to try to live with this structural constraint, and adapt our algorithm accordingly. For instance:
Coqに私たちの区分をそれにもかかわらず定義するために、最初の解決は、この構造的拘束を受け入れ、私たちのアルゴリズムを従って適応させようとすることです。例えば:
Fixpoint div x y := match x with
| 0 => 0
| S x' =>
let z := div x' y in
if (S z)*y ? x then S z else z
end.
Knowing the quotient for the predecessor of x can indeed be used to infer the quotient for x. But proceeding this way leads to a costly test repeated x times. This is a common situation with Coq: intended algorithms can be adapted to be “structural”, but this may result in an awkward and/or less efficient algorithm.
xの前任者のための商を知ることはxのための商を推論するために確かに使用されるかもしれません。しかし、この方法が高価なテストヘ導く進行はx回を繰り返しました。これはCoqを備えた共通の状況です:意図したアルゴリズムは「構造の」であるのに適している場合がある。しかし、これは厄介でかつ、またはそれほど効率的でないアルゴリズムに帰着するかもしれません。
Command Extraction div can then be used to convert this division to Ocaml code:
その後、コマンド抽出臆病者はこの区分をOcamlコードに変換するために使用することができます:
let rec div x y =
match x with
| O -> O
| S x' ->
let z = div x' y in
if le_lt_dec (mult (S z) y) x then S z else z
This first extracted div highlights the fact that on basic Coq functions, extraction is mainly performing a straightforward syntactic translation. But even on such a simple function, some proof elimination occurs during extraction. In fact, comparison le_lt_dec a b is not producing a mere boolean, but rather a proof-carrying boolean type {a<=b}+{b<a}, which is an inductive type internally named sumbool, with two constructors left and right both having a proof as parameter, here respectively a proof of ab and a proof of b<a. Extraction removes these proofs, hence obtaining an extracted sumbool datatype with two constant constructors, isomorphic to bool. In order to get precisely the extracted code shown above, one could then teach Coq to take advantage of this isomorphism, via : Extract Inductive sumbool => bool [ true false ].
この最初の抽出された臆病者は、基礎的なCoq機能においては、抽出が主として真直ぐな構文的な翻訳を行なっているという事実を強調します。しかし、そのような単関数においてさえ、ある耐えられる除去が抽出中に生じます。実際、bが生産していない比較le_lt_dec、1つの、単なる、ブール、しかしやや証明を持ったブールのタイプ{a<=b}+{b<a}(それは内部にsumboolという名の誘導のタイプである)、2人の建設者と、パラメーターとして証明を持っている左および正しい両方、ここでそれぞれ、abの証拠およびbの証拠<a。抽出は、2人の一定の建設者(boolに同形)と抽出されたsumboolデータ・タイプを従って得て、これらの証拠を削除します。その後、抽出されたコードを正確に上に示すと、一つは、次のものによって、この同形を利用することをCoqに教えることができました:誘導のsumbool=>boolを抽出する[真実、誤りの]。
One should note that the proof elimination done during extraction is based on earlier declarations by the user (or by the library designer). Here, proof-carrying boolean {a<=b}+{b<a} is exactly isomorphic to logical disjunction ab _ b<a (instead of left and right, constructors are named or introl and or intror). Simply, the former is declared in the logical world named Prop and is pruned during extraction whereas the latter is declared in Set, the world of Coq programs, and simply loses at extraction the logical parameters of its constructors. Similarly, two existential types coexist in Coq: the logical one 9x:A,P x and the informative one { x:A | P x }.
人は、抽出中に行われた耐えられる除去がユーザ(あるいはライブラリー・デザイナーによって)によって初期の宣言に基づくと述べるべきです。ここに、証明を持ったブールの{a<=b}+{b<a}は、ab_論理的な分離bに正確に同形です<1つの(の代わりに、左、そして右、建設者は指名されます、あるいはintrol、あるいはintror)。単に、前者は支柱という名の論理的な世界の中で宣言され、抽出中に除去される、後者がセット (Coqプログラムの世界)の中で宣言され、単に抽出でその建設者の論理的なパラメーターを失います。同様に、2つの存在のタイプがCoqに共存します: 論理的な1の9x:A A、P xおよび有益な1の{x:|P x}。
##2.2 A division with an explicit counter
Let’s now try to implement a function closer to our intended division algorithm, instead of the ad-hoc structural version of the last section. A solution is to artificially add a new structurally decreasing parameter that will control the number of allowed recursive calls. Here for instance, if y6=0, it is clear that at most x successive subtractions can occur before the algorithm stops. A common presentation is to separate the function to iterate div_F from the actual counterbased recursive iterator div loop. The main function div is then a simple call to div loop with the right initial counter value.
今最後のセクションの特別な構造のバージョンの代わりに、私たちの意図した除法定理に近い機能をインプリメントしようとしましょう。解決は、許可された再帰呼び出しの数をコントロールする、新しい構造上減少するパラメーターを人為的に加えることです。ここで、y6=0ならば、例えば、アルゴリズムが止まる前に、ほとんどのxの連続の減法のそれが生じる場合があることは明らかです。共通のプレゼンテーションは、実際のcounterbasedの再帰的な iterator臆病者ループからのdiv_Fを繰り返す機能を分離することです。主機能臆病者は、そのとき正しい最初の対価を備えた臆病者ループへの単純な呼び出しです。
Definition div_F div x y := if y <=? x then S (div (x-y) y) else 0.
Fixpoint div_loop (n:nat) :=
match n with
| 0 => fun _ _ => 0
| S n => div_F (div_loop n)
end.
Definition div x y := div_loop x x y.
One more time, extraction is straightforward and mostly amounts to replacing Coq keywords with Ocaml ones. The counter, whose type is nat, is kept by the extraction, even though it is morally useless for the computation. At the same time, removing it and replacing div loop by an unbounded loop would change the semantics of the program at least for y=0: with the above definition, div 5 0 computes to 5, while a Ocaml version without counter would loop forever. As a consequence, the extraction cannot be expected to detect and remove automatically such a “useless” parameter.
もう1回、抽出は真直ぐで、CoqキーワードをOcamlものに取り替えることにほとんどなります。たとえそれが計算には道義的に役立たなくても、カウンター(そのタイプはnatである)は抽出によって維持されます。同時に、それを削除し臆病者ループを非束縛のループに取り替えることは、少なくともy=0 とプログラムの意味論を交換するでしょう:上記の定義で、臆病者5 0は5まで計算します。その一方でカウンターのないOcamlバージョンは永久にループしているかもしれません。結果として、抽出は、そのような「役立たない」パラメーターを自動的に検知し削除することとは予想することができません。
Using such an explicit counter is often an interesting compromise: the written Coq code is not exactly what we intended in the first place, but is close to it, there is no complex internal Coq object as with the methods we will study in the next sections, computations can be done both in Coq and after extraction, and the additional cost induced by the presence of the counter is often modest. Here for instance the x value would have been computed anyway. Another example of this technique can be found in module Numtheory of the Standard Library, where a gcd function is defined on binary numbers thanks to a counter that can be the depth (i.e. the logarithm) of these binary numbers.
そのような明示的なカウンターの使用は多くの場合面白い妥協です:書かれたCoqコードはまさに私たちがまず第1に意図したものでないがそれに接近しています。私たちが次のセクションの中で勉強する方法でほど複雑な内部Coqオブジェクトはなく、計算をCoqの中で、および抽出の後に行うことができます。また、カウンターの存在によって引き起こされた追加費用は多くの場合適度です。ここで、例えば、x価値はとにかく計算されていたでしょう。この技術の別の例は、標準ライブラリー(これらの2進数の深さ(つまり対数)になりえるカウンターのおかげで2進数の上で最大公約数機能は定義される)のモジュール Numtheoryで見つけることができます。
##2.3 A division by general recursion, historical approach
We can in fact build a Coq div function that will produce exactly the intended algorithm after extraction. Before presenting the modern ways of writing such a function with two frameworks recently added to Coq, let us first mention the historical approach. For a long time, the only possibility has been to play with accessibility predicates and induction principles such as well founded induction. In this case, recursive functions do satisfy the structural constraint of Coq, not via their regular arguments, but rather via an additional logical argument expressing that some quantity is accessible. Recursive calls can then be done on quantities that are more easily accessible than before. This extra logical parameter is then meant to disappear during extraction. In practice, non-trivial functions are impossible to write as a whole with this approach, due to the numerous logical details to provide. Such functions are hence built piece by piece using Coq interactive tactics, as for proofs. Reasoning a posteriori on the body of such functions is also next to impossible, so key properties of these functions are to be attached to their output, via post-conditions { a:A | P a }. Pre-conditions can also be added to restrict functions on a certain domain: for instance, div will be defined only for y6=0. Here comes the complete specification of our div and its implementation in a proof-like style:
私たちは、抽出の後に意図したアルゴリズムを正確に生産するCoq臆病者機能を実際構築することができます。Coqに最近加えられた2つのフレームワークでそのような機能を書く現代式を示す前に、歴史上のアプローチに最初に言及しましょう。長い間、ただ一つの可能性は、よく設立された誘導のようなアクセシビリティ述語および誘導法則で遊ぶことでした。この場合、帰納的関数は、それらの規則的な議論によるではなくCoqの構造的拘束を満たします。しかし、もっと正確に言えば、論理的に議論を付加的に表現することによって、そのある量はアクセス可能です。その後、再帰呼び出しは、以前よりより容易にアクセス可能な量の上で行うことができます。その後、この余分な論理的なパラメーターは、抽出中に消えるのが目的です。実際上、このアプローチで全体として不自明な機能を書くことは、提供するべき多数の論理的な詳細により不可能です。そのような機能は証拠に関しては、ひとつひとつCoqの対話型の戦術を用いて、従って構築されます。そのような機能の主要部について帰納的に説得することはさらにあります、の隣りに、不可能、したがって、これらの機能の基本性質はポスト条件{aによって、それらの出力に付けられることになっています:1つの|P}。前条件もある領域上の機能を制限するために付け加えることができます:例えば、臆病者はy6=0のためにのみ定義されるでしょう。さあ、耐えられる類似のスタイルで私たちの臆病者とそのインプリメンテーションの完全明細書が来ました:
Definition div : 8x y, y <> 0 ! { z | z*y x < (S z)*y }.
Proof.
induction x as [x Hrec] using (well_founded_induction lt_wf).
intros y Hy.
destruct (y ? x) as [Hyx|Hyx]. (* do we have yx or x<y ? *)
(* first case: yx *)
assert (Hxy : x-y < x) by omega.
destruct (Hrec (x-y) Hxy y Hy) as [z Hz]. (* ie: let z = div (x-y) y *)
exists (S z); simpl in *; omega. (* ie: z+1 fits as (div x y) *)
(* second case: x<y *)
exists 0; omega.
Defined.
We use lt_wf, which states that < is well-founded on natural numbers. When combined with well founded induction, this allows us to perform recursive calls at will on any strictly smaller numbers. Doing such a recursive call can be quite cumbersome: for calling Hrec on x-y, we need to have already built a proof Hxy stating that x-y < x. Without additional help such as comments, it is also very tedious to keep track on the algorithm used in such a proof. Fortunately, extraction can do it for us:
私たちはlt_wfを使用します、それは次のことを述べる<自然数上で事実に基づいています。よく設立された誘導と結合した時、これは私たちが再帰呼び出しを行なうことを可能にします、で、どの上でも厳密にするだろう、少数。そのような再帰呼び出しを行うことは全く厄介かもしれません:x-yの上の Hrecを呼ぶために、私たちは、そのx-yを述べる耐えられるHxyを既に構築する必要がありました<x。コメントのような補足支援なしで、そのような証明の中で使用されるアルゴリズム上で軌跡を維持することはさらに非常に退屈です。幸運にも、抽出は私たちのためにそれをすることができます:
let rec div x y =
if le_lt_dec y x then S (div (minus x y) y) else O
##2.4 A division by general recursion with the Russell framework
The function-as-proof paradigm of the last section can be used on a relatively large scale, see for instance union and the few other non-structural operations on well-balanced trees in early versions of module FSetAVL in the Standard Library. But such Coq functions are hardly readable and maintainable, consume lots of resources during their definitions and in practice almost always fail to compute in Coq.
最後のセクションの耐えられる(として、機能)パラダイムの上で使用することができます、1つの、比較的大規模、例えば見る、結合、および標準ライブラリーのモジュールFSetAVLの初期のバージョン中の釣合の取れている木に対する他のわずかの非組織的なオペレーション。しかし、そのようなCoq機能はほとんど判読可能でなく維持可能です。また、それらの定義中に多くの資源を消費し、Coqの中でほとんど常に計算しません。
Recent versions of Coq include Russell, a framework due to M. Sozeau [10] that greatly eases the design of general recursive and/or dependently-typed functions. With this framework, bodies of functions can be written without being bothered by proof parts or by structural constraints. Simply, such definitions are fully accepted by Coq only when some corresponding proof obligations have been proved later on. For instance:
Coqの最近のバージョンはラッセル(一般的な再帰的な機能および(または)従属的にタイプされた機能のデザインを非常に緩和する M.Sozeau[10]によるフレームワーク)を含んでいます。このフレームワークで、機能の身体は耐えられる部分、あるいは構造的拘束によって悩まされずに書くことができます。単に、いくつかの対応する耐えられる義務が後で証明された場合に限り、そのような定義はCoqによって完全に受理されます。例えば:
Definition id (n:nat) := n.
Program Fixpoint div (x:nat)(y:nat | y <> 0) { measure id x }
: { z | z*y x < (S z)*y }
:= if y <=? x then S (div (x-y) y) else 0.
Next Obligation. (* Measure decreases on recursive call : x-y < x *)
unfold id; simpl; omega.
Qed.
Next Obligation. (* Post-condition enforcement : z*y x < (S z)*y *)
destruct_call div; simpl in *; omega.
Qed.
After this definition and the proofs of corresponding obligations, a Coq object div is added to the environment, mixing the pure algorithm and the logical obligations. This object is similar to the dependently-typed div of the previous section, and its extraction produces the very same Ocaml code.
対応する義務のこの定義および証拠の後、Coqオブジェクト臆病者は純粋なアルゴリズムおよび論理的な義務を混合して、環境に加えられます。このオブジェクトは前のセクションの従属的にタイプされた臆病者に似ています。また、その抽出は非常に同じOcamlコードを生産します。
Russell framework can be seen as a sort of anti-extraction, in the spirit of C. Parent’s earlier works [9]. Even if it is still considered as experimental, it is already quite usable. For instance, we have a version of FSetAVL where the aforementioned non-structural operations on well-balanced trees are written and proved using Russell.
ラッセル・フレームワークはC.親の初期の工場[9]の精神で一種の反抽出と見なすことができます。実験のこととまだ見なされても、それは既に全く使用可能です。例えば、ラッセルを使用して、釣合の取れている木に対する前述の非組織的なオペレーションが書かれており証明される場合、私たちは、 FSetAVLのバージョンを持っています。
##2.5 A division by general recursion with the Function framework An alternative framework can also be used to define our div function: Function, due to J. Forest and alii [4]. It is similar to Russell to some extent: algorithms can be written in a natural way, while proof obligations may have to be solved afterwards. Here, as for Russell, these proof obligations are trivial:
代替フレームワークも私たちの臆病者機能を定義するために使用することができます:J.森林およびalii[4]による機能。それは、ラッセルにある程度まで似ています:耐えられる義務が後で解決されていなければならないかもしれない一方、アルゴリズムは自然な方法で書くことができます。ここで、ラッセルに関しては、これらの耐えられる義務は重要でありません:
Function div (x y:nat)(Hy: y <> 0) { measure id x } : nat :=
if y <=? x then S (div (x-y) y Hy) else 0.
Proof.
intros; unfold id; omega.
Defined.
Moreover, as for Russell, this framework builds complex internal Coq objects, and extraction of these objects produces back precisely the expected code. But unlike Russell, Function is not meant to manipulate dependent types: in particular the y6=0 pre-condition is possible here only since it is passed unmodified to the recursive call. On the contrary, Function focuses on the ease of reasoning upon functions defined with it, see for instance the functional induction tactics, allowing to prove separately properties of div that would have been postconditions with Russell. Once again, the sensitive operations on well-balanced trees have be successfully tried and defined using Function.
さらに、ラッセルに関しては、このフレームワークは複雑な内部Coqオブジェクトを構築します。また、これらのオブジェクトの抽出は後ろに正確に生産します、予期されたコード。しかし、ラッセルと異なり、機能は、依存するタイプを操作するのが目的ではありません:ただそれが再帰呼び出しに未変更に渡されるので、y6=0前条件は特に、ここで可能です。これに反して、機能はそれで定義された機能について説得する容易さに注目します、ラッセルとの postconditionsになっていたであろう臆病者の特性を別々に証明することを可能にして、例えば機能的な誘導戦術を見ます。もう一度、釣合の取れている木に対する敏感なオペレーションは持っています、成功裡に機能を使用して、試みられ定義される。