如何限制Haskell中的开放世界假设

sem*_*lon 6 haskell typeclass

为了提高我对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
Run Code Online (Sandbox Code Playgroud)

现在,如果我尝试做某些事情,multiply 1 1我希望结果值是明确的.因为获得某种类型的唯一方法Num (Unit u a)是设置uNoUnit.其余的a应由违约规则处理.

不幸的是,由于Haskell的开放世界假设,一些邪恶的人可能决定即使一个有单位的数字应该是一个有效的Num实例,即使这样的事情会违反,(*) :: a -> a -> a因为数字与单位的乘法不适合那种类型的签名.

现在,开放世界的假设并非不合理,特别是因为Haskell报告不禁止孤立实例.但在这种特殊情况下,我确实想告诉GHC,Num实例的唯一有效幻像单元类型UnitNoUnit.

有没有办法明确说明这一点,并且在某种情况下,不允许孤儿实例允许GHC放宽开放世界的假设?

当尝试使用部分依赖键入使我的程序更安全时,这种事情已经出现了几次.每当我想指定一个NumIsStringIsList基础方案实例,然后使用自定义值或函数来获取所有其他可能的情况.

dfe*_*uer 14

你无法关闭开放世界的假设,但有一些方法可以限制它,包括这个时间.在您的情况下,问题是您编写Num实例的方式:

instance Num a => Num (Unit NoUnit a)
Run Code Online (Sandbox Code Playgroud)

你真正想要的是什么

instance (Num a, u ~ NoUnit) => Num (Unit u a)
Run Code Online (Sandbox Code Playgroud)

这样,当GHC看到它需要时Num (Unit u) a,它得出的结论是它需要Num au ~ NoUnit.你写它的方式,你留下了一些额外的实例的可能性.

将类型构造函数转换=>为左边的等式约束的权限通常很有用.