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

8

问题

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

目标是:

  1. 为变量和一般表达式拥有不同的类型 - 某些函数可能会应用于变量而不是复杂表达式。 例如,定义运算符:=的类型可能为(:=) :: 变量 -> 表达式 -> 定义,并且不应该可以将复杂表达式作为其左侧参数传递(尽管应该可以将变量作为其右侧参数传递,无需显式转换)。
  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这样的表示法,因为:=应该定义一个变量而不是整个左侧表达式。

1
也许你可以使用一个 class FromVar e,其中包含一个方法 fromVar :: Variable -> e,并为 ExpressionVariable 提供实例,然后让你的变量具有多态类型 x :: FromVar e => e 等。由于我现在正在使用手机,所以我还没有测试过这个方法的效果。 - Mor A.
我不确定FromVar类型类会如何帮助。我想避免显式转换,同时保持ExprNum的实例。我编辑了问题并添加了一个我想要实现的符号示例。 - Maciej Bendkowski
1个回答

7

为了利用多态而不是亚型(因为在 Haskell 中这是你拥有的全部),不要认为“一个变量是一个表达式”,而是“变量和表达式都具有某些共同的操作”。这些操作可以放入类型类中:

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)

有趣,谢谢!我曾考虑过在短暂的时刻将变量和表达式都隐藏在存在类型后面,但是我拒绝了这个想法,因为它引入了额外的符号,即您的V。最初这不是我想要的,但也许我太草率地就把它排除了... 可能我无法摆脱不透明的V。另外,如何创建V (forall e. HasVar e => e)的实例呢?在 Coq 中,我会使用类型计算和对归纳类型的模式匹配,但在 Haskell 中如何实现尚不清楚。 - Maciej Bendkowski
1
你可以以某种方式获取一个 w :: Variable 并对其应用 fromVarvariable = (\w -> V (fromVar w)) <$> (_TODO_ :: Solver Variable) - Li-yao Xia
1
“V”可以通过不定类型避免,但这仍然是一个正在进行的工作。或者,我们可以将“variable”显式地采用多态参数并使用续体来传递,而不是间接地使用“(>>=)”。 - Li-yao Xia

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接