假设我有一个参数类型的定义是这样的。
newtype FancyComplex a b = FancyComplex (a, b)
我打算永远不把这个新类型用于除数字参数以外的任何其他参数。 我的意思是,无论我做什么实现,我都知道参数 a
和 b
将永远是一个实例 Num
.
我在这个问题中看到,你可以这样做。在新类型定义中可以使用类型约束吗?
{-# LANGUAGE RankNTypes #-}
newtype (Num a, Num b) => FancyComplex 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')
但是,没有GHC会告诉我说我没有执行。Num
要求.所以我每次都被迫这样做。
instance (Num a, Num b) => StupidClass (FancyComplex a b) where
add (FancyComplex (a, b)) (FancyComplex (a', b')) = FancyComplex (a+a', b+b')
在newtype定义中写约束的作用就是迫使我每次都要明确地写约束。好吧,这在我忘记的情况下还是很有用的,但当然,我不希望每次都要重写约束。
我怎样才能自动地、隐式地从newtype定义中继承约束条件呢,这可能吗,如果不可能,有什么理由吗?
目前,我的弱工作方法是定义一个类型别名。type FancyComplexReqs a b = (Num a, Num b)
谢谢你
至少在不改变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
所以我们必须要有这样的条件。我只能看到这些选项。
这个... +
函数存储在 FancyComplex
值。这也行得通,但Haskell报告要求这个 newtype
来拥有相同的内存中的对的表示。没有额外指针的空间。
的 Num a, Num b
约束被隐式地添加到实例定义中,因为我们在实现中需要它。这样做是可行的,但如果是显式的不是更好吗?拥有隐式约束会使实例更难读,因为即使看起来没有约束,也会有一个约束。
现在,有一个可能的替代方案:如果你想要方案 1,并且你对不同的运行时内存表示方式没有意见,那么使用一个 data
来代替。
data FancyComplex a b where
FancyComplex :: (Num a, Num b) => a -> b -> FancyComplex a b
这样一来,每一个值都会存储自己的指针到 Num
例。这将需要更多的内存,但对于你的应用来说,这也许不是问题。
将约束编码到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')
你必须切换到 data
从 newtype
因为约束变成了字典,而字典是有运行时表示的。然而,通过这样做,我们可以摆脱你的元组,这样可以节省多达 data
费用: