Jadi saya memikirkannya sedikit lagi dan membuat beberapa kemajuan. Berikut adalah langkah pertama dalam pengkodean sistem Martin-Löf yang sangat sederhana (tetapi tidak konsisten) Set : Set
dalam gaya kombinasi. Ini bukan cara yang baik untuk menyelesaikannya, tetapi ini adalah tempat termudah untuk memulai. Sintaks dari teori tipe ini hanyalah lambda-kalkulus dengan anotasi tipe, tipe-Pi, dan himpunan semesta.
Teori Jenis Target
Demi kelengkapan, saya akan sampaikan aturannya. Validitas konteks hanya mengatakan Anda dapat membangun konteks dari kosong dengan menghubungkan variabel segar yang menempati Set
s.
G |- valid G |- S : Set
. |- valid G, x:S |- valid
Dan sekarang kita dapat mengatakan bagaimana mensintesis tipe untuk istilah dalam konteks tertentu, dan bagaimana mengubah tipe sesuatu hingga perilaku komputasi dari istilah yang dikandungnya.
G |- valid G |- S : Set G |- T : Pi S \ x:S -> Set
G |- Set : Set G |- Pi S T : Set
G |- S : Set G, x:S |- t : T x G |- f : Pi S T G |- s : S
G |- \ x:S -> t : Pi S T G |- f s : T s
G |- valid G |- s : S G |- T : Set
G |- x : S G |- s : T
Dalam variasi kecil dari aslinya, saya telah menjadikan lambda satu-satunya operator pengikat, jadi argumen kedua Pi harus menjadi fungsi yang menghitung cara tipe pengembalian bergantung pada input. Menurut konvensi (misalnya di Agda, tapi sayangnya tidak di Haskell), cakupan lambda meluas ke kanan sejauh mungkin, jadi Anda sering dapat membiarkan abstraksi tidak dikurung ketika itu adalah argumen terakhir dari operator tingkat tinggi: Anda bisa lihat saya melakukannya itu dengan Pi. Jenis Agda Anda (x : S) -> T
menjadi Pi S \ x:S -> T
.
( Digression . Anotasi jenis pada lambda diperlukan jika Anda ingin dapat mensintesis jenis abstraksi. Jika Anda beralih ke pemeriksaan jenis sebagai modus operandi, Anda masih memerlukan anotasi untuk memeriksa beta-redex seperti (\ x -> t) s
, karena Anda tidak punya cara untuk menebak jenis bagian dari keseluruhan. Saya menyarankan perancang modern untuk memeriksa jenis dan mengecualikan beta-redex dari sintaks.)
( Digresi . Sistem ini tidak konsisten karena Set:Set
memungkinkan pengkodean berbagai "paradoks pembohong". Ketika Martin-Löf mengajukan teori ini, Girard mengiriminya pengkodeannya dalam Sistem U miliknya yang tidak konsisten. Paradoks berikutnya yang disebabkan oleh Hurkens adalah konstruksi beracun paling rapi yang kita tahu.)
Sintaks dan Normalisasi Kombinator
Bagaimanapun, kami memiliki dua simbol tambahan, Pi dan Set, jadi kami mungkin mengelola terjemahan kombinasi dengan S, K dan dua simbol tambahan: Saya memilih U untuk alam semesta dan P untuk produk.
Sekarang kita dapat mendefinisikan sintaks kombinatori tanpa tipe (dengan variabel bebas):
data SKUP = S | K | U | P deriving (Show, Eq)
data Unty a
= C SKUP
| Unty a :. Unty a
| V a
deriving (Functor, Eq)
infixl 4 :.
Perhatikan bahwa saya telah menyertakan sarana untuk memasukkan variabel bebas yang diwakili oleh tipe a
dalam sintaks ini. Selain sebagai refleks di pihak saya (setiap sintaks yang sesuai dengan namanya adalah monad gratis dengan return
variabel embedding dan >>=
substitusi perfoming), akan berguna untuk mewakili tahap perantara dalam proses mengubah istilah dengan mengikat ke bentuk kombinasinya.
Berikut normalisasi:
norm :: Unty a -> Unty a
norm (f :. a) = norm f $. a
norm c = c
($.) :: Unty a -> Unty a -> Unty a
C S :. f :. a $. g = f $. g $. (a :. g)
C K :. a $. g = a
n $. g = n :. norm g
infixl 4 $.
(Latihan bagi pembaca adalah untuk menentukan tipe untuk persis bentuk normal dan mempertajam tipe operasi ini.)
Mewakili Teori Jenis
Sekarang kita dapat mendefinisikan sintaks untuk teori tipe kita.
data Tm a
= Var a
| Lam (Tm a) (Tm (Su a))
| Tm a :$ Tm a
| Pi (Tm a) (Tm a)
| Set
deriving (Show, Functor)
infixl 4 :$
data Ze
magic :: Ze -> a
magic x = x `seq` error "Tragic!"
data Su a = Ze | Su a deriving (Show, Functor, Eq)
Saya menggunakan representasi indeks de Bruijn dengan cara Bellegarde dan Hook (seperti yang dipopulerkan oleh Bird dan Paterson). Tipe Su a
memiliki satu elemen lebih dari a
, dan kami menggunakannya sebagai tipe variabel bebas di bawah pengikat, dengan Ze
sebagai variabel terikat baru dan Su x
merupakan representasi bergeser dari variabel bebas lama x
.
Menerjemahkan Istilah ke Kombinator
Dan setelah itu selesai, kami memperoleh terjemahan biasa, berdasarkan abstraksi braket .
tm :: Tm a -> Unty a
tm (Var a) = V a
tm (Lam _ b) = bra (tm b)
tm (f :$ a) = tm f :. tm a
tm (Pi a b) = C P :. tm a :. tm b
tm Set = C U
bra :: Unty (Su a) -> Unty a
bra (V Ze) = C S :. C K :. C K
bra (V (Su x)) = C K :. V x
bra (C c) = C K :. C c
bra (f :. a) = C S :. bra f :. bra a
Mengetik Kombinasi
Terjemahan menunjukkan cara kami menggunakan kombinator, yang memberi kami cukup petunjuk tentang jenis seharusnya. U
dan P
hanya mengatur konstruktor, jadi, menulis jenis yang tidak diterjemahkan dan mengizinkan "notasi Agda" untuk Pi, kita harus memiliki
U : Set
P : (A : Set) -> (B : (a : A) -> Set) -> Set
The K
combinator digunakan untuk mengangkat nilai dari beberapa jenis A
untuk fungsi konstan selama beberapa jenis lainnya G
.
G : Set A : Set
K : (a : A) -> (g : G) -> A
The S
combinator digunakan untuk aplikasi angkat lebih jenis, di mana semua bagian mungkin tergantung.
G : Set
A : (g : G) -> Set
B : (g : G) -> (a : A g) -> Set
S : (f : (g : G) -> (a : A g) -> B g a ) ->
(a : (g : G) -> A g ) ->
(g : G) -> B g (a g)
Jika Anda melihat pada tipe S
, Anda akan melihat bahwa itu secara tepat menyatakan aturan aplikasi kontekstual dari teori tipe, jadi itulah yang membuatnya cocok untuk mencerminkan konstruksi aplikasi. Itu tugasnya!
Kami kemudian memiliki aplikasi hanya untuk hal-hal tertutup
f : Pi A B
a : A
f a : B a
Tapi ada halangan. Saya telah menulis tipe-tipe kombinator dalam teori tipe biasa, bukan teori tipe kombinatoris. Untungnya, saya memiliki mesin yang akan menerjemahkan.
Sistem Jenis Kombinasi
U : U
P : PU(S(S(KP)(S(S(KP)(SKK))(S(KK)(KU))))(S(KK)(KU)))
G : U
A : U
K : P[A](S(S(KP)(K[G]))(S(KK)(K[A])))
G : U
A : P[G](KU)
B : P[G](S(S(KP)(S(K[A])(SKK)))(S(KK)(KU)))
S : P(P[G](S(S(KP)(S(K[A])(SKK)))(S(S(KS)(S(S(KS)(S(KK)(K[B])))(S(KK)(SKK))))
(S(S(KS)(KK))(KK)))))(S(S(KP)(S(S(KP)(K[G]))(S(S(KS)(S(KK)(K[A])))
(S(S(KS)(KK))(KK)))))(S(S(KS)(S(S(KS)(S(KK)(KP)))(S(KK)(K[G]))))
(S(S(KS)(S(S(KS)(S(KK)(KS)))(S(S(KS)(S(S(KS)(S(KK)(KS)))
(S(S(KS)(S(KK)(KK)))(S(KK)(K[B])))))(S(S(KS)(S(S(KS)(S(KK)(KS)))(S(KK)(KK))))
(S(KK)(KK))))))(S(S(KS)(S(S(KS)(S(KK)(KS)))(S(S(KS)(S(KK)(KK)))
(S(S(KS)(KK))(KK)))))(S(S(KS)(S(S(KS)(S(KK)(KS)))(S(KK)(KK))))(S(KK)(KK)))))))
M : A B : U
M : B
Jadi begitulah, dalam semua kemuliaan yang tak terbaca: presentasi kombinasi dari Set:Set
!
Masih ada sedikit masalah. Sintaks sistem tidak memberi Anda cara untuk menebak G
, A
dan B
parameter untuk S
dan yang serupa K
, hanya dari istilah. Sejalan dengan itu, kami dapat memverifikasi derivasi pengetikan secara algoritme, tetapi kami tidak bisa hanya memeriksa istilah kombinator seperti yang kami bisa dengan sistem aslinya. Apa yang mungkin berhasil adalah meminta masukan kepada pemeriksa mesin untuk memuat penjelasan jenis pada penggunaan S dan K, yang secara efektif mencatat penurunan. Tapi itu cacing kaleng lainnya ...
Ini adalah tempat yang bagus untuk berhenti, jika Anda sudah cukup bersemangat untuk memulai. Sisanya adalah masalah "di balik layar".
Menghasilkan Jenis Combinator
Saya menghasilkan tipe kombinatoris tersebut menggunakan terjemahan abstraksi braket dari istilah teori tipe yang relevan. Untuk menunjukkan bagaimana saya melakukannya, dan membuat posting ini tidak sepenuhnya sia-sia, izinkan saya menawarkan peralatan saya.
Saya dapat menulis jenis kombinator, yang sepenuhnya diabstraksi atas parameternya, sebagai berikut. Saya menggunakan pil
fungsi praktis saya , yang menggabungkan Pi dan lambda untuk menghindari pengulangan tipe domain, dan cukup membantu memungkinkan saya menggunakan ruang fungsi Haskell untuk mengikat variabel. Mungkin Anda hampir bisa membaca yang berikut ini!
pTy :: Tm a
pTy = fmap magic $
pil Set $ \ _A -> pil (pil _A $ \ _ -> Set) $ \ _B -> Set
kTy :: Tm a
kTy = fmap magic $
pil Set $ \ _G -> pil Set $ \ _A -> pil _A $ \ a -> pil _G $ \ g -> _A
sTy :: Tm a
sTy = fmap magic $
pil Set $ \ _G ->
pil (pil _G $ \ g -> Set) $ \ _A ->
pil (pil _G $ \ g -> pil (_A :$ g) $ \ _ -> Set) $ \ _B ->
pil (pil _G $ \ g -> pil (_A :$ g) $ \ a -> _B :$ g :$ a) $ \ f ->
pil (pil _G $ \ g -> _A :$ g) $ \ a ->
pil _G $ \ g -> _B :$ g :$ (a :$ g)
Dengan ini didefinisikan, saya mengekstrak subterm terbuka yang relevan dan menjalankannya melalui terjemahan.
Perangkat Pengkodean de Bruijn
Berikut cara membangunnya pil
. Pertama, saya mendefinisikan kelas Fin
set ite, yang digunakan untuk variabel. Setiap set tersebut memiliki emb
edding yang menjaga konstruktor ke dalam set di atas, ditambah top
elemen baru , dan Anda dapat membedakannya: embd
fungsi tersebut memberi tahu Anda jika suatu nilai ada dalam gambar emb
.
class Fin x where
top :: Su x
emb :: x -> Su x
embd :: Su x -> Maybe x
Kita dapat, tentu saja, membuat contoh Fin
untuk Ze
danSuc
instance Fin Ze where
top = Ze
emb = magic
embd _ = Nothing
instance Fin x => Fin (Su x) where
top = Su top
emb Ze = Ze
emb (Su x) = Su (emb x)
embd Ze = Just Ze
embd (Su x) = fmap Su (embd x)
Sekarang saya bisa mendefinisikan kurang atau sama, dengan operasi yang melemah .
class (Fin x, Fin y) => Le x y where
wk :: x -> y
The wk
Fungsi harus menanamkan unsur-unsur x
sebagai terbesar unsur y
, sehingga hal-hal ekstra dalam y
lebih kecil, dan dengan demikian di de hal indeks Bruijn, terikat lebih lokal.
instance Fin y => Le Ze y where
wk = magic
instance Le x y => Le (Su x) (Su y) where
wk x = case embd x of
Nothing -> top
Just y -> emb (wk y)
Dan setelah Anda menyelesaikannya, sedikit penipuan kelas atas melakukan sisanya.
lam :: forall x. Tm x -> ((forall y. Le (Su x) y => Tm y) -> Tm (Su x)) -> Tm x
lam s f = Lam s (f (Var (wk (Ze :: Su x))))
pil :: forall x. Tm x -> ((forall y . Le (Su x) y => Tm y) -> Tm (Su x)) -> Tm x
pil s f = Pi s (lam s f)
Fungsi tingkat tinggi tidak hanya memberi Anda istilah yang mewakili variabel, ini memberi Anda hal yang kelebihan beban yang menjadi representasi yang benar dari variabel dalam lingkup mana pun di mana variabel tersebut terlihat. Artinya, fakta bahwa saya mengalami kesulitan dalam membedakan cakupan yang berbeda berdasarkan jenisnya memberikan informasi yang cukup kepada pemeriksa mesin ketik Haskell untuk menghitung pergeseran yang diperlukan untuk terjemahan ke representasi de Bruijn. Mengapa memelihara anjing dan menggonggong sendiri?