Eu gostaria de aprender mais sobre programação concatenativa através da criação de uma pequena linguagem simples, baseada na pilha e seguindo o paradigma concatenativo.
Infelizmente, não encontrei muitos recursos relacionados a linguagens concatenativas e sua implementação; portanto, desculpe-me antecipadamente por minha possível ingenuidade.
Portanto, defini minha linguagem como uma sequência simples de concatenação de funções, representada no AST como uma lista:
data Operation
= Concat [Operation]
| Quotation Operation
| Var String
| Lit Literal
| LitOp LiteralOperation
data Literal
= Int Int
| Float Float
data LiteralOperation
= Add | Sub | Mul | Div
O seguinte programa, 4 2 swap dup * +
(correspondente a 2 * 2 + 4
) uma vez analisado, fornecerá o seguinte AST:
Concat [Lit (Int 4), Lit (Int 2), Var "swap", Var "dup", LitOp Mul, LitOp Add]
Agora eu tenho que inferir e verificar os tipos.
Eu escrevi este sistema de tipos:
data Type
= TBasic BasicType -- 'Int' or 'Float'
| TVar String -- Variable type
| TQuoteE String -- Empty stack, noted 'A'
| TQuote String Type -- Non empty stack, noted 'A t'
| TConc Type Type -- A type for the concatenation
| TFun Type Type -- The type of functions
É aí que entra minha pergunta, porque não sei que tipo inferir dessa expressão. O tipo resultante é óbvio, é Int
, mas eu não sei como realmente verificar totalmente esse programa no nível de tipo.
No começo, como você pode ver acima, pensei em um TConc
tipo que representa concatenação da mesma maneira que o TFun
tipo representa uma função, porque no final a sequência de concatenação forma uma função única.
Outra opção, que ainda não explorei, seria aplicar a regra de inferência da composição da função a cada elemento dessa sequência de expressão. Não sei como isso funcionaria com o baseado em pilha.
A questão é a seguinte: como fazemos? Qual algoritmo usar e qual abordagem no nível de tipo deve ser preferida?