Haskell 中的隐式静态类型转换(强制)

Mac*_*ski 9 haskell casting type-conversion typeclass ghc

问题

考虑以下 Haskell 中的设计问题。我有一个简单的符号 EDSL,我想在其中表达变量和一般表达式(多元多项式),例如x^2 * y + 2*z + 1. 另外,我想在表达式上表达某些符号方程,比如x^2 + 1 = 1,以及定义,比如x := 2*y - 2

目标是:

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

换句话说,我希望将变量隐式静态类型转换(强制)转换为表达式。现在,我知道在 Haskell 中没有隐式类型转换。尽管如此,某些面向对象的编程概念(在这种情况下是简单的继承)在 Haskell 的类型系统中是可以表达的,无论有没有语言扩展。如何在保持轻量级语法的同时满足以上两点?甚至有可能吗?

讨论

很明显,这里的主要问题是Num类型限制,例如

(+) :: Num a => a -> a -> a

原则上,可以为变量和表达式编写单个(广义)代数数据类型。然后,可以这样写:=,区分左侧表达式并且只接受变量构造函数,否则会出现运行时错误。然而,这不是一个干净的、静态的(即编译时)解决方案......

例子

理想情况下,我想实现轻量级语法,例如

computation = do
  x <- variable
  t <- variable

  t |:=| x^2 - 1
  solve (t |==| 0)
Run Code Online (Sandbox Code Playgroud)

特别是,我想禁止像t + 1 |:=| x^2 - 1因为它:=应该给出一个变量的定义而不是整个左侧表达式的符号 。

Li-*_*Xia 8

要利用多态而不是子类型(因为这就是 Haskell 中的所有内容),不要认为“变量就是表达式”,而是“变量和表达式都有一些共同的操作”。这些操作可以放在一个类型类中:

class HasVar e where fromVar :: Variable -> e

instance HasVar Variable where fromVar = id
instance HasVar Expression where ...
Run Code Online (Sandbox Code Playgroud)

然后,与其铸造事物,不如使事物多态。如果有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
Run Code Online (Sandbox Code Playgroud)

骨架使下面的代码类型检查:https ://gist.github.com/Lysxia/da30abac357deb7981412f1faf0d2103

computation :: Solver ()
computation = do
  V x <- variable
  V t <- variable
  t |:=| x^2 - 1
  solve (t |==| 0)
Run Code Online (Sandbox Code Playgroud)