【发布时间】:2016-08-30 07:12:24
【问题描述】:
为了提高我对 GHC 扩展的了解,我决定尝试使用单位来实现数字,而我想做的一件事是将数字文字用于无单位值。但由于 Haskell 所做的开放世界假设,结果证明这不是很实用。以下是我无法工作的一个最小示例:
data Unit u a = Unit a
data NoUnit
instance Num a => Num (Unit NoUnit a) where
-- (+) (*) (-) abs signum not important
fromInteger = Unit . fromInteger
type family Multiply a b where
Multiply NoUnit NoUnit = NoUnit
multiply :: Num a => Unit u1 a -> Unit u2 a -> Unit (Multiply u1 u2) a
multiply (Unit a) (Unit b) = Unit $ a * b
现在,如果我尝试执行 multiply 1 1 之类的操作,我希望得到的值是明确的。因为获得Num (Unit u a) 类型的唯一方法是将u 设置为NoUnit。剩下的a 应该通过默认规则来处理。
不幸的是,由于 Haskell 的开放世界假设,一些邪恶的人可能认为即使是具有单位的数字也应该是有效的 Num 实例,即使这样的事情会违反 (*) :: a -> a -> a 作为数字与单位不适合该类型签名。
现在,开放世界的假设并非不合理,特别是因为 Haskell 报告并未禁止孤儿实例。但在这种特殊情况下,我真的很想告诉 GHC,Num 的 Unit 实例的唯一有效幻像单元类型是 NoUnit。
有什么方法可以明确说明这一点,并且在某种程度上,不允许孤儿实例允许 GHC 完全放松开放世界假设?
在尝试使用部分依赖类型使我的程序更安全时,这种事情已经出现过几次。每当我想为基本情况指定 Num 或 IsString 或 IsList 实例时,然后使用自定义值或函数来获取所有其他可能的情况。
【问题讨论】:
-
这无关,但我希望您的
Multiply类型系列具有Multiply NoUnit y = y和Multiply x NoUnit = x而不是Multiply NoUnit NoUnit = NoUnit。此外,Adam Gundry 得出结论,GHC 的内置设施不足以完成良好的测量单位工作。你应该看看uom-plugin,一个支持这种东西的类型检查器插件。 -
@dfeuer 感谢您的帮助,但我的实际代码不是这样的,这只是一个最小的失败示例。我的实际代码非常不同,使用
DataKinds。它似乎适用于所有 SI 单位,只需跟踪 7 个基本 SI 单位中的每一个被组合到每个值中的数量。