Problema
Considere o seguinte problema de design no Haskell. Eu tenho um EDSL simbólico simples, no qual quero expressar variáveis e expressões gerais (polinômios multivariados), como x^2 * y + 2*z + 1
. Além disso, quero expressar certas equações simbólicas sobre expressões, por exemplo x^2 + 1 = 1
, bem como definições , como x := 2*y - 2
.
O objetivo é:
- Tenha um tipo separado para variáveis e expressões gerais - certas funções podem ser aplicadas a variáveis e não expressões complexas. Por exemplo, uma definição de operador
:=
pode ser do tipo(:=) :: Variable -> Expression -> Definition
e não deve ser possível passar uma expressão complexa como parâmetro lado esquerdo (embora ele deve ser possível passar uma variável como parâmetro do lado direito, sem conversão explícita ) . - Tenha expressões como uma instância
Num
, para que seja possível promover literais inteiros para expressões e usar uma notação conveniente para operações algébricas comuns como adição ou multiplicação sem a introdução de alguns operadores auxiliares de wrapper.
Em outras palavras, eu gostaria de ter uma conversão de tipo implícita e estática (coerção) de variáveis para expressões. Agora, eu sei que, como tal, não há projeções implícitas de tipo em Haskell. No entanto, certos conceitos de programação orientada a objetos (herança simples, neste caso) são expressáveis no sistema de tipos de Haskell, com ou sem extensões de linguagem. Como eu poderia satisfazer os dois pontos acima, mantendo uma sintaxe leve? Isso é possível?
Discussão
É claro que o principal problema aqui é Num
a restrição de tipo, por exemplo
(+) :: Num a => a -> a -> a
Em princípio, é possível escrever um único tipo de dados algébrico (generalizado) para variáveis e expressões. Então, pode-se escrever :=
de tal maneira que a expressão do lado esquerdo seja discriminada e apenas um construtor de variáveis seja aceito, com um erro em tempo de execução. No entanto, essa não é uma solução limpa e estática (ou seja, em tempo de compilação) ...
Exemplo
Idealmente, gostaria de obter uma sintaxe leve, como
computation = do
x <- variable
t <- variable
t |:=| x^2 - 1
solve (t |==| 0)
Em particular, quero proibir a notação como
t + 1 |:=| x^2 - 1
uma vez que :=
deve dar uma definição de uma variável e não uma expressão inteira do lado esquerdo.
FromVar
classe seria útil. Eu quero evitar conversões explícitas, mantendo Expr
uma instância de Num
. Editei a pergunta adicionando um exemplo de uma notação que gostaria de obter.
class FromVar e
com um métodofromVar :: Variable -> e
e fornecer instâncias paraExpression
eVariable
, em seguida, fazer com que suas variáveis tenham tipos polimórficosx :: FromVar e => e
etc. Não testei como isso funciona desde que estou no telefone agora.