问题
考虑在Haskell中的以下设计问题。我有一个简单的符号EDSL,我想要表达变量和一般表达式(多元多项式),例如x^2 * y + 2*z + 1
。此外,我想要表达某些关于表达式的符号方程,例如x^2 + 1 = 1
,以及定义,例如x := 2*y - 2
。
目标是:
- 为变量和一般表达式拥有不同的类型 - 某些函数可能会应用于变量而不是复杂表达式。
例如,定义运算符
:=
的类型可能为(:=) :: 变量 -> 表达式 -> 定义
,并且不应该可以将复杂表达式作为其左侧参数传递(尽管应该可以将变量作为其右侧参数传递,无需显式转换)。 - 使表达式成为
Num
的实例,这样就可以将整数字面量提升为表达式,并使用方便的符号表示常见的代数运算,如加法或乘法,而不需要引入一些辅助包装运算符。
换句话说,我希望将变量隐式和静态类型转换(coercion)为表达式。现在,我知道在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
这样的表示法,因为:=
应该定义一个变量而不是整个左侧表达式。
class FromVar e
,其中包含一个方法fromVar :: Variable -> e
,并为Expression
和Variable
提供实例,然后让你的变量具有多态类型x :: FromVar e => e
等。由于我现在正在使用手机,所以我还没有测试过这个方法的效果。 - Mor A.FromVar
类型类会如何帮助。我想避免显式转换,同时保持Expr
是Num
的实例。我编辑了问题并添加了一个我想要实现的符号示例。 - Maciej Bendkowski