Problema
Considera il seguente problema di progettazione in Haskell. Ho un semplice EDSL simbolico in cui voglio esprimere variabili ed espressioni generali (polinomi multivariati) come x^2 * y + 2*z + 1
. Inoltre, voglio esprimere alcune equazioni simboliche sulle espressioni, per esempio x^2 + 1 = 1
, così come le definizioni , come x := 2*y - 2
.
L'obiettivo è:
- Hanno un tipo separato per variabili ed espressioni generali: alcune funzioni potrebbero essere applicate a variabili e non a espressioni complesse. Ad esempio, un operatore di definizione
:=
potrebbe essere di tipo(:=) :: Variable -> Expression -> Definition
e non dovrebbe essere possibile passare un'espressione complessa come parametro sul lato sinistro (sebbene dovrebbe essere possibile passare una variabile come parametro sul lato destro, senza cast esplicito ) . - Hanno espressioni in un'istanza di
Num
, in modo che sia possibile promuovere letterali interi in espressioni e usare una pratica notazione per operazioni algebriche comuni come addizione o moltiplicazione senza introdurre alcuni operatori wrapper ausiliari.
In altre parole, vorrei avere un cast di tipo implicito e statico (coercizione) di variabili alle espressioni. Ora, so che come tale, non ci sono cast di tipi impliciti in Haskell. Tuttavia, alcuni concetti di programmazione orientata agli oggetti (eredità semplice, in questo caso) sono espressi nel sistema di tipi di Haskell, con o senza estensioni del linguaggio. Come potrei soddisfare entrambi i punti sopra mantenendo una sintassi leggera? È anche possibile?
Discussione
È chiaro che il problema principale qui è Num
la restrizione del tipo, ad es
(+) :: Num a => a -> a -> a
In linea di principio, è possibile scrivere un singolo tipo di dati algebrico (generalizzato) sia per le variabili che per le espressioni. Quindi, si potrebbe scrivere :=
in modo tale che l'espressione sul lato sinistro è discriminata e viene accettato solo un costruttore di variabili, con un errore di runtime altrimenti. Questa tuttavia non è una soluzione pulita, statica (cioè in fase di compilazione) ...
Esempio
Idealmente, vorrei ottenere una sintassi leggera come
computation = do
x <- variable
t <- variable
t |:=| x^2 - 1
solve (t |==| 0)
In particolare, desidero vietare la notazione come
t + 1 |:=| x^2 - 1
poiché :=
dovrebbe dare una definizione di una variabile e non un'intera espressione sul lato sinistro.
FromVar
sarebbe utile la tabella dei tipi. Voglio evitare cast espliciti mantenendo Expr
un'istanza di Num
. Ho modificato la domanda aggiungendo un esempio di una notazione che vorrei ottenere.
class FromVar e
con un metodofromVar :: Variable -> e
e fornire istanze perExpression
eVariable
, quindi le tue variabili hanno tipi polimorficix :: FromVar e => e
ecc.