Haskell 中的隐式静态类型转换(强制)
Implicit, static type cast (coercion) in Haskell
问题
考虑Haskell中的以下设计问题。我有一个简单的符号 EDSL,我想在其中表达变量和一般表达式(多元多项式),例如 x^2 * y + 2*z + 1
。此外,我想在表达式上表达某些符号方程,比如 x^2 + 1 = 1
,以及 definitions,例如 x := 2*y - 2
.
目标是:
- 变量和一般表达式有一个单独的类型 - 某些
函数可能应用于变量而不是复杂的表达式。
例如,definition operator
:=
可能是类型
(:=) :: Variable -> Expression -> Definition
不应该
可以将复杂的表达式作为其左侧传递
参数(尽管应该可以传递一个变量作为它的参数
右侧参数,无显式转换).
- 让表达式成为
Num
的一个实例,这样就可以
将整数文字提升为表达式并使用方便的
常见代数运算的符号,如加法或
乘法而不引入一些辅助包装运算符。
换句话说,我想要 implicit 和 static 变量到表达式的类型转换(强制)。现在,我知道 Haskell 中没有隐式类型转换。然而,某些面向对象的编程概念(在本例中为简单继承)在 Haskell 的类型系统中是 可表达的 ,有或没有语言扩展。如何在保持轻量级语法的同时满足以上两点?有可能吗?
讨论
很明显,这里的主要问题是Num
的类型限制,例如
(+) :: Num a => a -> a -> a
原则上,可以为变量和表达式编写一个(通用的)代数数据类型。然后,可以这样写 :=
,即区分左侧表达式,只接受变量构造函数,否则会出现 运行 时错误。然而,这不是一个干净的、静态的(即编译时)解决方案...
例子
理想情况下,我想实现一个轻量级的语法,例如
computation = do
x <- variable
t <- variable
t |:=| x^2 - 1
solve (t |==| 0)
特别是,我想禁止像这样的符号
t + 1 |:=| x^2 - 1
因为 :=
应该给出变量的定义而不是整个左侧表达式。
要利用多态性而不是子类型化(因为这就是您在 Haskell 中的全部),不要考虑 "a variable is an expression",而是 "both variables and expressions have some operations in common"。这些操作可以放在类型 class:
中
class HasVar e where fromVar :: Variable -> e
instance HasVar Variable where fromVar = id
instance HasVar Expression where ...
然后,不是铸造东西,而是让东西多态。如果你有v :: forall e. HasVar e => e
,它既可以用作表达式又可以用作变量。
example :: (forall e. HasVar e => e) -> Definition
example v = (v := v) -- v can be used as both Variable and Expression
where
(:=) :: Variable -> Expression -> Definition
制作以下代码的骨架类型检查:https://gist.github.com/Lysxia/da30abac357deb7981412f1faf0d2103
computation :: Solver ()
computation = do
V x <- variable
V t <- variable
t |:=| x^2 - 1
solve (t |==| 0)
问题
考虑Haskell中的以下设计问题。我有一个简单的符号 EDSL,我想在其中表达变量和一般表达式(多元多项式),例如 x^2 * y + 2*z + 1
。此外,我想在表达式上表达某些符号方程,比如 x^2 + 1 = 1
,以及 definitions,例如 x := 2*y - 2
.
目标是:
- 变量和一般表达式有一个单独的类型 - 某些
函数可能应用于变量而不是复杂的表达式。
例如,definition operator
:=
可能是类型(:=) :: Variable -> Expression -> Definition
不应该 可以将复杂的表达式作为其左侧传递 参数(尽管应该可以传递一个变量作为它的参数 右侧参数,无显式转换). - 让表达式成为
Num
的一个实例,这样就可以 将整数文字提升为表达式并使用方便的 常见代数运算的符号,如加法或 乘法而不引入一些辅助包装运算符。
换句话说,我想要 implicit 和 static 变量到表达式的类型转换(强制)。现在,我知道 Haskell 中没有隐式类型转换。然而,某些面向对象的编程概念(在本例中为简单继承)在 Haskell 的类型系统中是 可表达的 ,有或没有语言扩展。如何在保持轻量级语法的同时满足以上两点?有可能吗?
讨论
很明显,这里的主要问题是Num
的类型限制,例如
(+) :: Num a => a -> a -> a
原则上,可以为变量和表达式编写一个(通用的)代数数据类型。然后,可以这样写 :=
,即区分左侧表达式,只接受变量构造函数,否则会出现 运行 时错误。然而,这不是一个干净的、静态的(即编译时)解决方案...
例子
理想情况下,我想实现一个轻量级的语法,例如
computation = do
x <- variable
t <- variable
t |:=| x^2 - 1
solve (t |==| 0)
特别是,我想禁止像这样的符号
t + 1 |:=| x^2 - 1
因为 :=
应该给出变量的定义而不是整个左侧表达式。
要利用多态性而不是子类型化(因为这就是您在 Haskell 中的全部),不要考虑 "a variable is an expression",而是 "both variables and expressions have some operations in common"。这些操作可以放在类型 class:
中class HasVar e where fromVar :: Variable -> e
instance HasVar Variable where fromVar = id
instance HasVar Expression where ...
然后,不是铸造东西,而是让东西多态。如果你有v :: forall e. HasVar e => e
,它既可以用作表达式又可以用作变量。
example :: (forall e. HasVar e => e) -> Definition
example v = (v := v) -- v can be used as both Variable and Expression
where
(:=) :: Variable -> Expression -> Definition
制作以下代码的骨架类型检查:https://gist.github.com/Lysxia/da30abac357deb7981412f1faf0d2103
computation :: Solver ()
computation = do
V x <- variable
V t <- variable
t |:=| x^2 - 1
solve (t |==| 0)