Saya bereksperimen dengan sistem tipe murni dalam kubus lambda Barendregt, khususnya dengan yang paling kuat, Kalkulus Konstruksi. Sistem ini memiliki macam *
dan BOX
. Sebagai catatan, di bawah ini saya menggunakan sintaks konkret dari Morte
alat https://github.com/Gabriel439/Haskell-Morte-Library yang dekat dengan kalkulus lambda klasik.
Saya melihat kita dapat meniru tipe induktif dengan semacam pengkodean seperti Gereja (alias isomorfisma Boehm-Berarducci untuk tipe data aljabar). Sebagai contoh sederhana saya menggunakan tipe Bool = ∀(t : *) -> t -> t -> t
dengan konstruktor True = λ(t : *) -> λ(x : t) -> λ(y : t) -> x
dan False = λ(t : *) -> λ(x : t) -> λ(y : t) -> y
.
Saya melihat bahwa tipe fungsi level-level Bool -> T
adalah isomorfik untuk pasangan tipe Product T T
dengan Product = λ(A : *) -> λ(B : *) -> ∀(t : *) -> (A -> B -> t) -> t
parametrik modulo klasik dengan fungsi if : Bool -> λ(t : *) -> t -> t -> t
yang sebenarnya merupakan identitas.
Semua pertanyaan di bawah ini adalah tentang representasi tipe dependen Bool -> *
.
Saya dapat dibagi
D : Bool -> *
menjadi sepasangD True
danD False
. Apakah ada cara kanonik untuk menciptakanD
lagi? Saya ingin mereproduksi isomosismeBool -> T = Product T T
dengan analog fungsiif
pada level type tetapi saya tidak dapat menulis fungsi ini sesederhana aslinyaif
karena kita tidak dapat melewatkan jenis dalam argumen seperti tipe.Saya menggunakan jenis tipe induktif dengan dua konstruktor untuk menyelesaikan pertanyaan (1). Deskripsi tingkat tinggi (Agda-style) adalah tipe berikut (digunakan sebagai ganti tipe-level
if
)data BoolDep (T : *) (F : *) : Bool -> * where DepTrue : T -> BoolDep T F True DepFalse : F -> BoolDep T F False
dengan penyandian berikut di PTS / CoC:
λ(T : *) -> λ(F : *) -> λ(bool : Bool ) -> ∀(P : Bool -> *) -> ∀(DepTrue : T -> P True ) -> ∀(DepFalse : F -> P False ) -> P bool
Apakah pengkodean saya di atas benar?
Saya dapat menuliskan konstruktor untuk
BoolDep
seperti kode ini untukDepTrue : ∀(T : *) -> ∀(F : *) -> T -> BoolDep T F True
:λ(T : *) -> λ(F : *) -> λ(arg : T ) -> λ(P : Bool -> *) -> λ(DepTrue : T -> P True ) -> λ(DepFalse : F -> P False ) -> DepTrue arg
tapi saya tidak bisa menuliskan fungsi terbalik (atau fungsi apa pun di arah terbalik). Apa itu mungkin? Atau haruskah saya menggunakan representasi lain untuk BoolDep
menghasilkan isomorfisme BoolDep T F True = T
?
sumber
Jawaban:
Anda tidak dapat melakukan ini menggunakan pengkodean Gereja tradisional untuk
Bool
:... karena Anda tidak dapat menulis fungsi tipe (berguna):
Alasan mengapa, seperti yang Anda catat, adalah bahwa Anda tidak dapat meneruskan
*
sebagai argumen pertama#Bool
, yang pada gilirannya berarti bahwaTrue
danFalse
argumen mungkin bukan tipe.Setidaknya ada tiga cara Anda bisa menyelesaikan ini:
Gunakan Kalkulus Konstruksi Induktif. Maka Anda dapat menggeneralisasi jenis
#Bool
ke:... dan kemudian Anda akan instantiate
n
ke1
, yang berarti Anda bisa masuk*₀
sebagai argumen kedua, yang akan ketik-cek karena:... jadi Anda dapat menggunakan
#Bool
untuk memilih antara dua jenis.Tambahkan satu jenis lagi:
Maka Anda akan mendefinisikan
#Bool₂
tipe terpisah seperti ini:Ini pada dasarnya adalah kasus khusus dari Kalkulus konstruksi Induktif, tetapi menghasilkan kode yang kurang dapat digunakan kembali karena sekarang kita harus mempertahankan dua definisi terpisah
#Bool
, satu untuk setiap jenis yang ingin kami dukung.Menyandikan
#Bool₂
langsung dalam Kalkulus Konstruksi sebagai:Jika tujuannya adalah untuk menggunakan ini secara langsung di dalam yang tidak dimodifikasi
morte
maka hanya pendekatan # 3 yang akan bekerja.sumber
#Bool₁
ke#Bool₂