Estoy experimentando con sistemas de tipo puro en el cubo lambda de Barendregt, específicamente con el más poderoso, el cálculo de construcciones. Este sistema tiene géneros *
y BOX
. Solo para el registro, a continuación estoy usando la sintaxis concreta de la Morte
herramienta https://github.com/Gabriel439/Haskell-Morte-Library que está cerca del cálculo lambda clásico.
Veo que podemos emular tipos inductivos mediante algún tipo de codificación similar a la Iglesia (también conocido como isomorfismo Boehm-Berarducci para tipos de datos algebraicos). Para un ejemplo simple, uso type Bool = ∀(t : *) -> t -> t -> t
con los constructores True = λ(t : *) -> λ(x : t) -> λ(y : t) -> x
y False = λ(t : *) -> λ(x : t) -> λ(y : t) -> y
.
Veo que el tipo de funciones de nivel de término Bool -> T
es isomorfo a pares de tipos Product T T
con Product = λ(A : *) -> λ(B : *) -> ∀(t : *) -> (A -> B -> t) -> t
paramétrica de módulo clásico por medio de una función if : Bool -> λ(t : *) -> t -> t -> t
que en realidad es identidad.
Todas las preguntas a continuación serán sobre representaciones de tipos dependientes Bool -> *
.
Me puedo dividir
D : Bool -> *
en par deD True
yD False
. ¿Existe la forma canónica de crear deD
nuevo? Quiero reproducir el isomosfismoBool -> T = Product T T
mediante un análogo de funciónif
a nivel de tipo, pero no puedo escribir esta función tan simple como originalif
porque no podemos pasar tipos en argumentos como tipos.Utilizo una especie de tipo inductivo con dos constructores para resolver la pregunta (1). La descripción de alto nivel (estilo Agda) es el siguiente tipo (se utiliza en lugar del nivel de tipo
if
)data BoolDep (T : *) (F : *) : Bool -> * where DepTrue : T -> BoolDep T F True DepFalse : F -> BoolDep T F False
con la siguiente codificación en PTS / CoC:
λ(T : *) -> λ(F : *) -> λ(bool : Bool ) -> ∀(P : Bool -> *) -> ∀(DepTrue : T -> P True ) -> ∀(DepFalse : F -> P False ) -> P bool
¿Mi codificación anterior es correcta?
Puedo escribir los constructores de
BoolDep
este código paraDepTrue : ∀(T : *) -> ∀(F : *) -> T -> BoolDep T F True
:λ(T : *) -> λ(F : *) -> λ(arg : T ) -> λ(P : Bool -> *) -> λ(DepTrue : T -> P True ) -> λ(DepFalse : F -> P False ) -> DepTrue arg
pero no puedo escribir la función inversa (o cualquier función en la dirección inversa). ¿Es posible? ¿O debería usar otra representación para BoolDep
producir un isomorfismo BoolDep T F True = T
?
fuente
Respuestas:
No puede hacer esto usando la codificación tradicional de la Iglesia para
Bool
:... porque no puedes escribir una función (útil) de tipo:
La razón por la cual, como notó, es que no puede pasar
*
como primer argumento#Bool
, lo que a su vez significa que los argumentosTrue
yFalse
pueden no ser tipos.Hay al menos tres formas de resolver esto:
Usa el cálculo de construcciones inductivas. Entonces podría generalizar el tipo de
#Bool
a:... y entonces sería una instancia
n
a1
, que significa que podría pasar*₀
como segundo argumento, lo que escriba-cheque porque:... entonces podría usar
#Bool
para seleccionar entre dos tipos.Agregue un tipo más:
Entonces definirías un
#Bool₂
tipo separado como este:Este es esencialmente un caso especial del cálculo de las construcciones inductivas, pero produce un código menos reutilizable ya que ahora debemos mantener dos definiciones separadas
#Bool
, una para cada tipo que deseamos admitir.Codificar
#Bool₂
directamente dentro del cálculo de construcciones como:Si el objetivo es usar esto directamente sin modificar
morte
, solo funcionará el enfoque n. ° 3.fuente
#Bool₁
a#Bool₂