Estou experimentando sistemas de tipo puro no cubo lambda de Barendregt, especificamente com o mais poderoso, o Cálculo de Construções. Este sistema tem tipos *
e BOX
. Apenas para constar, abaixo, estou usando a sintaxe concreta da Morte
ferramenta https://github.com/Gabriel439/Haskell-Morte-Library, que fica próxima ao cálculo lambda clássico.
Vejo que podemos emular tipos indutivos por algum tipo de codificação semelhante à Igreja (também conhecido como isomorfismo de Boehm-Berarducci para tipos de dados algébricos). Para um exemplo simples, eu uso type Bool = ∀(t : *) -> t -> t -> t
com os construtores True = λ(t : *) -> λ(x : t) -> λ(y : t) -> x
e False = λ(t : *) -> λ(x : t) -> λ(y : t) -> y
.
Vejo que o tipo de funções de nível de termo Bool -> T
é isomórfico para pares de tipo Product T T
com Product = λ(A : *) -> λ(B : *) -> ∀(t : *) -> (A -> B -> t) -> t
parametricidade clássica de módulo por meio de função if : Bool -> λ(t : *) -> t -> t -> t
que é de fato identidade.
Todas as perguntas abaixo serão sobre representações de tipos dependentes Bool -> *
.
Eu posso dividir
D : Bool -> *
em par deD True
eD False
. Existe a maneira canônica de criarD
novamente? Quero reproduzir o isomosfismoBool -> T = Product T T
por um análogo de funçãoif
no nível de tipo, mas não posso escrever essa função tão simples quanto o original,if
porque não podemos passar tipos em argumentos como tipos.Eu uso um tipo de tipo indutivo com dois construtores para resolver a questão (1). A descrição de alto nível (estilo Agda) é o seguinte tipo (usado em vez do nível de tipo
if
)data BoolDep (T : *) (F : *) : Bool -> * where DepTrue : T -> BoolDep T F True DepFalse : F -> BoolDep T F False
com a seguinte codificação em PTS / CoC:
λ(T : *) -> λ(F : *) -> λ(bool : Bool ) -> ∀(P : Bool -> *) -> ∀(DepTrue : T -> P True ) -> ∀(DepFalse : F -> P False ) -> P bool
Minha codificação acima está correta?
Eu posso escrever os construtores
BoolDep
como 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
mas não posso escrever a função inversa (ou qualquer função na direção inversa). É possível? Ou devo usar outra representação para BoolDep
produzir um isomorfismo BoolDep T F True = T
?
fonte
Respostas:
Você não pode fazer isso usando a codificação tradicional da Igreja para
Bool
:... porque você não pode escrever uma função (útil) do tipo:
A razão pela qual, como você observou, é que você não pode passar
*
como o primeiro argumento para#Bool
, o que, por sua vez, significa que os argumentosTrue
eFalse
podem não ser tipos.Há pelo menos três maneiras de resolver isso:
Use o cálculo de construções indutivas. Em seguida, você pode generalizar o tipo de
#Bool
para:... e então você instanciar
n
a1
, o que significa que você pode passar*₀
como o segundo argumento, que tipo de verificação, porque:... então você pode usar
#Bool
para selecionar entre dois tipos.Adicione mais uma classificação:
Então você definiria um
#Bool₂
tipo separado como este:Este é essencialmente um caso especial das construções de Cálculo de Indutivo, mas produz um código menos reutilizável, pois agora devemos manter duas definições separadas de
#Bool
, uma para cada tipo que desejamos apoiar.Codifique
#Bool₂
diretamente no Cálculo de construções como:Se o objetivo é usar isso diretamente dentro de não modificado
morte
, apenas a abordagem nº 3 funcionará.fonte
#Bool₁
para#Bool₂