假设我有一个类型类Vec
,它实现了理论上的向量空间理论。
class Vec a where
(+) :: a -> a -> a
zero :: a
-- rest omitted
现在给出一个自然数n,我可以很容易地构造一个Vec
的实例,其基础类型是有理数列表的类型,它实现了维数为n的向量空间。我们在下面采用n = 3:
newtype RatList3 = RatList3 { list3 :: [Rational] }
instance Vec RatList3 where
v + w = RatList3 (zipWith (Prelude.+) (list3 v) (list3 w))
zero = RatList3 (take 3 (repeat 0))
对于另一个自然数,例如计算出的数,我可以写
f :: Int -> Int
f x = x * x -- some complicated function
n :: Int
n = f 2
newtype RatListN = RatListN { listN :: [Rational] }
instance Vec RatListN where
v + w = RatListN (zipWith (Prelude.+) (listN v) (listN w))
zero = RatListN (take n (repeat 0))
现在我有两种类型,一种用于维度3的向量空间,另一种用于维度为n的向量空间。但是,如果我想将我的instance Vec RatList?
形式的实例声明放在我不知道主程序最终使用的模块中,我有一个问题,因为类型RatList?
没有知道它属于哪个。
为解决这个问题,我尝试按以下方式做一些事情:
class HasDim a where
dim :: Int
instance (HasDim a, Fractional a) => Vec [a] where
v + w = ...
zero = take dim (repeat (fromRational 0))
-- in the main module
instance HasDim Rational where
dim = n -- some integer
当然,这不起作用,因为dim
中的HasDim
独立于类型变量a
,而instance (HasDim a) => Vec [a]
中不清楚哪个类型dim
1}}采取。我试图通过引入另一种类型来解决第一个问题:
newtype Dim a = Dim { idim :: Int }
然后我可以写
class HasDim a where
dim :: Dim a
但是,我不清楚如何在instance (HasDim a) => Vec [a] where
中使用它。此外,我的整个“解决方案”看起来相当麻烦,而提出的问题看起来很简单。 (我认为用C ++模板编写代码很容易。)
修改
我除了ephemient的答案,因为没有类型算术它以我想要的方式解决了我的问题。仅供参考,我的最终解决方案如下:
class Vec a where
zero :: a
-- ...
n :: Int
n = 10
newtype RatListN = RatListN [Rational]
instance Vec RatListN where
zero = RatListN . take n $ repeat 0
-- ...
答案 0 :(得分:2)
这似乎是type arithmetic可以为您提供所需内容的情况。
data Zero
data Succ a
type One = Succ Zero
type Two = Succ One
type Three = Succ Two
-- ...
class NumericType a where
toNum :: (Num b) => a -> b
instance NumericType Zero where
toNum = const 0
instance (NumericType a) => NumericType (Succ a) where
toNum (Succ a) = toNum a + 1
data RatList a b = RatList { list :: [b] }
instance (NumericType a, Num b) => Vec (RatList a b) where
zero = RatList . take (toNum (undefined :: a)) $ repeat 0
现在RatList Two Int
和RatList Three Int
是不同的类型。另一方面,这确实会阻止您在运行时实例化新的维度...