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.

目标是:

  1. 变量和一般表达式有一个单独的类型 - 某些 函数可能应用于变量而不是复杂的表达式。 例如,definition operator := 可能是类型 (:=) :: Variable -> Expression -> Definition 不应该 可以将复杂的表达式作为其左侧传递 参数(尽管应该可以传递一个变量作为它的参数 右侧参数,无显式转换).
  2. 让表达式成为 Num 的一个实例,这样就可以 将整数文字提升为表达式并使用方便的 常见代数运算的符号,如加法或 乘法而不引入一些辅助包装运算符。

换句话说,我想要 implicitstatic 变量到表达式的类型转换(强制)。现在,我知道 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)