Problema
Considere el siguiente problema de diseño en Haskell. Tengo un EDSL simple y simbólico en el que quiero expresar variables y expresiones generales (polinomios multivariados) como x^2 * y + 2*z + 1
. Además, quiero expresar ciertas ecuaciones simbólicas sobre expresiones, digamos x^2 + 1 = 1
, así como definiciones , como x := 2*y - 2
.
La meta es:
- Tenga un tipo separado para variables y expresiones generales: ciertas funciones pueden aplicarse a variables y no a expresiones complejas. Por ejemplo, un operador de definición
:=
podría ser de tipo(:=) :: Variable -> Expression -> Definition
y no debería ser posible pasar una expresión compleja como su parámetro del lado izquierdo (aunque debería ser posible pasar una variable como su parámetro del lado derecho, sin conversión explícita ) . - Tenga una instancia de expresiones
Num
, de modo que sea posible promover literales enteros a expresiones y usar una notación conveniente para operaciones algebraicas comunes como la suma o la multiplicación sin introducir algunos operadores auxiliares de envoltura.
En otras palabras, me gustaría tener un tipo de conversión implícita y estática (coerción) de variables a expresiones. Ahora, sé que, como tal, no hay tipos implícitos en Haskell. Sin embargo, ciertos conceptos de programación orientada a objetos (herencia simple, en este caso) se pueden expresar en el sistema de tipos de Haskell, con o sin extensiones de lenguaje. ¿Cómo podría satisfacer los dos puntos anteriores manteniendo una sintaxis ligera? ¿Es posible?
Discusión
Está claro que el principal problema aquí es Num
la restricción de tipo, por ejemplo
(+) :: Num a => a -> a -> a
En principio, es posible escribir un solo tipo de datos algebraicos (generalizados) para variables y expresiones. Entonces, se podría escribir :=
de tal manera, que la expresión del lado izquierdo se discrimina y solo se acepta un constructor de variables, de lo contrario, con un error de tiempo de ejecución. Sin embargo, esa no es una solución limpia y estática (es decir, tiempo de compilación) ...
Ejemplo
Idealmente, me gustaría lograr una sintaxis ligera como
computation = do
x <- variable
t <- variable
t |:=| x^2 - 1
solve (t |==| 0)
En particular, quiero prohibir la notación como
t + 1 |:=| x^2 - 1
ya que :=
debería dar una definición de una variable y no una expresión completa del lado izquierdo.
class FromVar e
con un métodofromVar :: Variable -> e
y proporcionar instancias paraExpression
yVariable
, luego, hacer que sus variables tengan tipos polimórficos,x :: FromVar e => e
etc. No he probado qué tan bien funciona desde que estoy en mi teléfono en este momento.FromVar
clase de tipos sería de ayuda. Quiero evitar lanzamientos explícitos mientras mantengoExpr
una instancia deNum
. Edité la pregunta agregando un ejemplo de una notación que me gustaría lograr.Respuestas:
Para aprovechar el polimorfismo en lugar de subtipar (porque eso es todo lo que tiene en Haskell), no piense que "una variable es una expresión", sino que "ambas variables y expresiones tienen algunas operaciones en común". Esas operaciones se pueden poner en una clase de tipo:
Luego, en lugar de lanzar cosas, haga las cosas polimórficas. Si es así
v :: forall e. HasVar e => e
, se puede usar como expresión y como variable.Esqueleto para hacer que el siguiente código escriba: https://gist.github.com/Lysxia/da30abac357deb7981412f1faf0d2103
fuente
V
. Inicialmente, eso no era lo que quería, pero tal vez fui demasiado rápido para descartarlo ... Probablemente no pueda deshacerme del opacoV
. En una nota relacionada, ¿cómo puedo crear una instancia deV (forall e . HasVar e => e)
? En Coq, usaría cálculos de tipo y coincidencia de patrones en un tipo inductivo, pero no está claro cómo lograrlo en Haskell.w :: Variable
de alguna manera y se aplicanfromVar
a la misma:variable = (\w -> V (fromVar w)) <$> (_TODO_ :: Solver Variable)
.V
podría evitarse con tipos impredecibles, pero eso sigue siendo WIP. O podemos hacervariable
tomar la continuación con un argumento polimórfico explícitamente en lugar de indirectamente a través de(>>=)
.