Tipe dependen sebagai argumen fungsi di Coq

Saya sangat baru mengenal Coq. Saya mencoba bereksperimen dengan tipe dependen Coq. Yang ingin saya lakukan hanyalah memasukkan bilangan genap ke suatu fungsi. Misalnya, dalam kode semu:

def next_even(n: {n: Integer | n is even}) :=
{
  add n 2
}

Lalu saya ingin menggunakan fungsi tersebut dengan argumen berbeda, seperti (dalam kode semu):

next_even(1)    // Coq should show an error
next_even(4)    // Coq should compute 6

Jadi, di Coq, saya ingin melakukan hal berikut:

Compute (next_even 1).
Compute (next_even 4).

Bagaimana cara membangunnya?


person arnobpl    schedule 14.11.2019    source sumber


Jawaban (1)


Berikut ini terjemahan langsung dari fungsi Anda di Coq:

From Coq Require Import Arith.

Definition add_two_even (n : {n : nat | Nat.even n = true}) : nat :=
  proj1_sig n + 1.

Perhatikan bahwa Anda memerlukan fungsi proj1_sig untuk mengekstrak n dari subset tipe {n : nat | Nat.even n = true}. Untuk menggunakan add_two_even, Anda juga perlu melakukan sebaliknya: beralih dari angka ke elemen {n | Nat.even n = true}. Di Coq, ini memerlukan pembuktian manual. Untuk nilai konkrit n, caranya mudah:

(* The next command fails with a type checking error *)
Fail Compute add_two_even (exist _ 1 eq_refl).
(* The next command succeeds *)
Compute add_two_even (exist _ 4 eq_refl).

Konstruktor exist membungkus nilai x dengan bukti P x, menghasilkan elemen bertipe subset {x | P x}. Suku eq_refl adalah bukti dari x = x untuk setiap nilai x. Jika n adalah bilangan konkrit, Coq dapat mengevaluasi Nat.even n dan mencari tahu apakah eq_refl merupakan bukti valid dari Nat.even n = true. Ketika n adalah 1, Nat.even n = false, dan pemeriksaan gagal, menyebabkan pesan kesalahan pertama. Ketika n adalah 4, pengecekan berhasil.

Segalanya menjadi lebih rumit ketika n bukan sebuah konstanta, melainkan ekspresi arbitrer. Sebuah bukti bahwa Nat.even n = true memerlukan alasan terperinci yang harus dipandu oleh pengguna. Misalnya, kita mengetahui bahwa Nat.even (n + n) = true untuk nilai n apa pun, tetapi Coq tidak. Jadi, untuk memanggil add_two_even pada sesuatu yang berbentuk n + n, kita perlu menunjukkan sebuah lemma.

Lemma add_n_n_even n : Nat.even (n + n) = true.
Proof.
induction n as [|n IH]; trivial.
now simpl; rewrite <- plus_n_Sm, IH.
Qed.

Definition foo (n : nat) :=
  add_two_even (exist _ (n + n) (add_n_n_even n)).

Ada beberapa alat untuk memfasilitasi gaya pemrograman ini, seperti plugin persamaan, namun yang umum kebijaksanaan dalam komunitas Coq adalah Anda harus menghindari tipe subset untuk fungsi seperti add_two_even, yang batasannya tidak menyederhanakan properti fungsi secara substansial.

Anda dapat menemukan banyak contoh penggunaan tipe subset yang baik di pustaka Komponen Matematika. Misalnya, perpustakaan menggunakan tipe subset untuk mendefinisikan tipe n.-tuple T dari daftar dengan panjang n, dan tipe 'I_n dari bilangan bulat yang dibatasi oleh n. Hal ini memungkinkan kita untuk mendefinisikan fungsi pengakses total tnth (t : n.-tuple T) (i : 'I_n) : T yang mengekstrak elemen ke i dari daftar t. Jika kita mendefinisikan pengakses ini untuk daftar dan bilangan bulat arbitrer, kita perlu meneruskan nilai default ke fungsi untuk dikembalikan ketika indeks berada di luar batas, atau mengubah tanda tangan fungsi sehingga mengembalikan nilai bertipe option T, yang menunjukkan bahwa fungsi bisa gagal mengembalikan nilai. Perubahan ini membuat properti fungsi pengakses lebih sulit untuk dinyatakan. Misalnya, perhatikan lemma eq_from_tnth, yang menyatakan bahwa dua daftar adalah sama jika semua elemennya sama:

eq_from_tnth:
  forall (n : nat) (T : Type) (t1 t2 : n.-tuple T),
    (forall i : 'I_n, tnth t1 i = tnth t2 i) -> t1 = t2

Pernyataan lemma untuk daftar arbitrer ini menjadi lebih rumit, karena memerlukan asumsi tambahan yang mengatakan bahwa kedua daftar tersebut berukuran sama. (Di sini, x0 adalah nilai default.)

eq_from_nth:
  forall (T : Type) (x0 : T) (s1 s2 : list T),
  size s1 = size s2 ->
  (forall i : nat, i < size s1 -> nth x0 s1 i = nth x0 s2 i) -> s1 = s2
person Arthur Azevedo De Amorim    schedule 14.11.2019
comment
Apa maksud Anda kebijaksanaan umum untuk tidak menggunakan tipe subset? Lalu apa yang harus Anda gunakan? Saya pikir tipe subset digunakan di mathcomp dan untuk alasan yang baik, mereka lebih mudah untuk dimanipulasi daripada tipe induktif yang diindeks. - person Théo Winterhalter; 14.11.2019
comment
Ya, perpustakaan mathcomp menyediakan pola penggunaan yang baik untuk tipe subset. Yang saya maksud adalah menggunakan tipe subset untuk fungsi seperti add_two_even umumnya merupakan ide yang buruk, di mana batasan tambahan pada tanda tangan mungkin tidak akan menyederhanakan properti tentang fungsi ini. Saya akan mencoba menjelaskannya dengan lebih baik dalam jawaban saya. - person Arthur Azevedo De Amorim; 14.11.2019