Estou trabalhando em um compilador para uma linguagem concatenativa e gostaria de adicionar suporte à inferência de tipo. Entendo Hindley-Milner, mas tenho aprendido a teoria dos tipos à medida que passo, por isso não tenho certeza de como adaptá-la. O sistema a seguir é sólido e decididamente inferível?
Um termo é um literal, uma composição de termos, uma cotação de um termo ou um primitivo.
Todos os termos denotam funções. Para duas funções e , , isto é, a justaposição indica a composição inversa. Os literais denotam funções niládicas.e 2 e 1
Os termos que não sejam composição têm regras básicas de tipo:
As regras de aplicação são notavelmente ausentes, pois as linguagens concatenativas não possuem.
Um tipo é um literal, uma variável de tipo ou uma função de pilhas para pilhas, em que uma pilha é definida como uma tupla aninhada à direita. Todas as funções são implicitamente polimórficas em relação ao "resto da pilha".
Esta é a primeira coisa que parece suspeita, mas não sei exatamente o que há de errado nisso.
Para facilitar a legibilidade e reduzir os parênteses, presumo que nos esquemas de tipos. Também usarei uma letra maiúscula para uma variável que denota uma pilha, em vez de um único valor.
Existem seis primitivos. Os cinco primeiros são bastante inócuos. dup
pega o valor mais alto e produz duas cópias. swap
altera a ordem dos dois principais valores. pop
descarta o valor superior. quote
pega um valor e produz uma cotação (função) que o retorna. apply
aplica uma cotação à pilha.
O último combinador, compose
deve pegar duas aspas e retornar o tipo de concatenação, ou seja, . Na linguagem concatenativa estaticamente tipada Cat , o tipo de é muito direto.compose
No entanto, esse tipo é muito restritivo: requer que a produção da primeira função corresponda exatamente ao consumo da segunda. Na realidade, você precisa assumir tipos distintos e depois unificá-los. Mas como você escreveria esse tipo?
Se você deixar denotar uma diferença de dois tipos, acho que você pode escrever o tipo corretamente.compose
Isto ainda é relativamente simples: compose
assume uma função e uma . Seu resultado consome acima do consumo de não produzido por e produz acima da produção de não consumido por . Isso dá a regra para a composição comum.f 2 : D → E B f 2 f 1 D f 1 f 2
No entanto, não sei se esse hipotético realmente corresponde a alguma coisa, e eu o persigo em círculos há tempo suficiente para achar que tomei uma decisão errada. Poderia ser uma simples diferença de tuplas?
Existe algo terrivelmente quebrado sobre isso que não estou vendo ou estou no caminho certo? (Provavelmente quantifiquei algumas dessas coisas incorretamente e também apreciaria correções nessa área.)
compose
é muito restritiva? Tenho a impressão de que isso está bem assim. (por exemplo, a restrição pode ser tratado por unificação como para aplicação em como no λ-cálculo)
twice
definido como dup compose apply
, que recebe uma cotação e a aplica duas vezes. [1 +] twice
está bom: você está compondo duas funções do tipo . Mas não é: se , o problema é , portanto a expressão é desaprovada, mesmo que deva ser válida e ter digite . É claro que a solução é colocar o qualificador no lugar certo, mas estou me perguntando principalmente como realmente escrever o tipo sem alguma definição circular. [pop] twice
compose