在 Haskell 中,如何将约束附加到参数化新类型,以便它们自动应用于使用它的任何 class 实例?

In Haskell, how to attach constraints to a parametric newtype, so that they automatically apply to any class instance that uses it?

假设我有一个这样定义的参数类型:

newtype FancyComplex a b = FancyComplex (a, b)

我打算永远不会将此新类型用于数字参数以外的任何其他参数。 我的意思是,对于我可能执行的任何实现,我知道参数 ab 将始终是 Num 的实例。

我在这个问题中读到你可以这样做:

{-# LANGUAGE RankNTypes #-}
newtype (Num a, Num b) => FancyComplex a b = FancyComplex (a, b)

然而这还不够。 如果我这样写任何 class:

class StupidClass x where add :: x -> x -> x

那我应该可以写

instance StupidClass (FancyComplex a b) where
    add (FancyComplex (a, b)) (FancyComplex (a', b')) = FancyComplex (a+a', b+b')

但是没有 GHC 会告诉我说我没有执行 Num 要求。 所以我每次都被迫这样做:

instance (Num a, Num b) => StupidClass (FancyComplex a b) where
    add (FancyComplex (a, b)) (FancyComplex (a', b')) = FancyComplex (a+a', b+b')

在新类型定义中编写约束所做的一切,就是迫使我每次都明确地编写约束。好的,这仍然有用,以防我忘记。 但是我当然希望不必每次都重写约束。

如何自动隐式继承新类型定义的约束? 这可能吗?如果不是,有什么不可以的原因吗?

目前我的弱解决方法是定义类型别名type FancyComplexReqs a b = (Num a, Num b)

谢谢

在 GADT 中对约束进行编码,如下所示:

{-# LANGUAGE GADTs #-}

data FancyComplex a b where
  FancyComplex :: (Num a, Num b) => a -> b -> FancyComplex a b

class StupidClass x where add :: x -> x -> x

instance StupidClass (FancyComplex a b) where
    add (FancyComplex a b) (FancyComplex a' b') = FancyComplex (a+a') (b+b')

您必须从 newtype 切换到 data,因为约束变成了字典,它确实具有运行时表示。但是,通过这样做,我们可以摆脱您的元组,从而节省 data 成本。

这个不能实现,至少不改变a的意思newtype:

newtype (Num a, Num b) => FancyComplex a b = FancyComplex (a, b)

instance StupidClass (FancyComplex a b) where
    add (FancyComplex (a, b)) (FancyComplex (a', b')) = FancyComplex (a+a', b+b')

在最后一行中,a+a' 需要函数 +,它是 Num 的一个方法,因此我们需要随时使用它。我只能看到这些选项:

  1. +函数存储在FancyComplex值中。这可行,但 Haskell 报告要求此 newtype 具有相同的内存对对表示形式。没有space追加指针

  2. Num a, Num b 约束隐式添加到实例定义中,因为我们在实现中需要它。这可以工作,但明确说明不是更好吗?具有隐式约束会使实例更难阅读,因为即使看起来 none.

  3. 也存在约束

现在,有一个可能的替代方案:如果您想要选项 1,并且您可以使用不同的运行时内存表示,那么请改用 data

data FancyComplex a b where
   FancyComplex :: (Num a, Num b) => a -> b -> FancyComplex a b

通过这种方式,每个值都将存储自己指向 Num 实例的指针。它将需要更多内存,但对于您的应用程序来说这可能不是问题。