Eksperymentuję z systemami czystego typu w sześcianie lambda Barendregta, a konkretnie z najsilniejszym z nich, Rachunkiem Konstrukcji. Ten system ma rodzaje *
i BOX
. Dla przypomnienia, poniżej używam konkretnej składni Morte
narzędzia https://github.com/Gabriel439/Haskell-Morte-Library, która jest zbliżona do klasycznego rachunku lambda.
Widzę, że możemy emulować typy indukcyjne za pomocą pewnego rodzaju kodowania podobnego do Kościoła (znanego również jako izomorfizm Boehm-Berarducci dla algebraicznych typów danych). Dla prostego przykładu używam typu Bool = ∀(t : *) -> t -> t -> t
z konstruktorami True = λ(t : *) -> λ(x : t) -> λ(y : t) -> x
i False = λ(t : *) -> λ(x : t) -> λ(y : t) -> y
.
Widzę, że typ funkcji na poziomie terminu Bool -> T
jest izomorficzny w stosunku do par typu Product T T
o klasycznej Product = λ(A : *) -> λ(B : *) -> ∀(t : *) -> (A -> B -> t) -> t
parametryczności modularnej za pomocą funkcji, if : Bool -> λ(t : *) -> t -> t -> t
która w rzeczywistości jest identyczna.
Wszystkie poniższe pytania dotyczą reprezentacji typów zależnych Bool -> *
.
Mogę podzielić
D : Bool -> *
na paręD True
iD False
. Czy istnieje kanoniczny sposób naD
ponowne utworzenie ? Chcę odtworzyć izomoszmizmBool -> T = Product T T
przez analog funkcjiif
na poziomie typu, ale nie mogę napisać tej funkcji tak prostej jak oryginał,if
ponieważ nie możemy przekazywać rodzajów w argumentach takich jak typy.Używam rodzaju indukcyjnego z dwoma konstruktorami do rozwiązania pytania (1). Opis wysokiego poziomu (w stylu Agda) to następujący typ (używany zamiast poziomu
if
)data BoolDep (T : *) (F : *) : Bool -> * where DepTrue : T -> BoolDep T F True DepFalse : F -> BoolDep T F False
z następującym kodowaniem w PTS / CoC:
λ(T : *) -> λ(F : *) -> λ(bool : Bool ) -> ∀(P : Bool -> *) -> ∀(DepTrue : T -> P True ) -> ∀(DepFalse : F -> P False ) -> P bool
Czy moje kodowanie powyżej jest prawidłowe?
Mogę zapisać konstruktory dla
BoolDep
takiego kodu dlaDepTrue : ∀(T : *) -> ∀(F : *) -> T -> BoolDep T F True
:λ(T : *) -> λ(F : *) -> λ(arg : T ) -> λ(P : Bool -> *) -> λ(DepTrue : T -> P True ) -> λ(DepFalse : F -> P False ) -> DepTrue arg
ale nie mogę zapisać funkcji odwrotnej (ani żadnej funkcji w kierunku odwrotnym). Czy to możliwe? Czy powinienem użyć innej reprezentacji BoolDep
do wytworzenia izomorfizmu BoolDep T F True = T
?
źródło
Odpowiedzi:
Nie możesz tego zrobić za pomocą tradycyjnego kodowania Kościoła dla
Bool
:... ponieważ nie możesz napisać (przydatnej) funkcji typu:
Powodem, dla którego, jak zauważyłeś, jest to, że nie możesz podać
*
pierwszego argumentu#Bool
, co z kolei oznacza, że argumentyTrue
iFalse
mogą nie być typami.Istnieją co najmniej trzy sposoby rozwiązania tego problemu:
Użyj rachunku konstrukcji indukcyjnych. Następnie możesz uogólnić typ
#Bool
na:... a potem będzie instancję
n
do1
, co oznacza, że może przejść w*₀
jako drugi argument, który typu check, ponieważ:... więc możesz użyć
#Bool
jednego z dwóch typów.Dodaj jeszcze jeden rodzaj:
Następnie zdefiniowałbyś osobny
#Bool₂
typ taki jak ten:Jest to zasadniczo szczególny przypadek rachunku konstrukcji indukcyjnych, ale produkuje mniej kodu wielokrotnego użytku, ponieważ teraz musimy zachować dwie oddzielne definicje
#Bool
, po jednej dla każdego rodzaju, który chcemy wspierać.Koduj
#Bool₂
bezpośrednio w rachunku konstrukcji jako:Jeśli celem jest użycie tego bezpośrednio w niezmodyfikowanym
morte
stanie, zadziała tylko podejście nr 3.źródło
#Bool₁
na#Bool₂