在类类中使用实例类型参数

时间:2015-04-30 15:27:28

标签: haskell typeclass

作为练习,我尝试创建一个Vector类型类作为练习:

class Vector v where
  vplus :: v -> v -> v
  vnegate :: v -> v

type V3 a = (a,a,a)

instance (Num a) => Vector (V3 a) where
  (a,b,c) `vplus` (d,e,f) = (a+d, b+e, c+f)
  vnegate (a,b,c) = ((-a), (-b), (-c))

我想在类型类上添加dot函数。在上面的V3示例中,我按如下方式实现它:

dot :: (Num a) => V3 a -> V3 a -> a
(a,b,c) `dot` (d,e,f) = a*d + b*e + c*f

但是,我似乎无法访问a中的类型参数Vector,因此我无法dot对{{1}进行操作我想要的方式。我如何访问Vector类型参数?

1 个答案:

答案 0 :(得分:1)

您希望使用TypeFamilies来创建关联类型:

{-# LANGUAGE TypeFamilies, TypeSynonymInstances, FlexibleInstances #-}

class Vector v where
    -- Declares a family of types called Item, parametrized on the
    -- instance v of Vector, and the kind of Item v must be *,
    -- meaning that it must be a type, not a type constructor
    -- (e.g. Maybe Int :: * vs Maybe :: * -> *)
    type family Item v :: *
    dot :: v -> v -> Item v
    ...

instance (Num a) => Vector (V3 a) where
    type Item (V3 a) = a
    dot (a, b, c) (d, e, f) = a*d + b*e + c*f
    ...

然后你可以做

> dot (1, 2, 3) ((4, 5, 6) :: V3 Int)
32

虽然我建议不要使用类型同义词实例,但最好还是使用数据类型:

data V3 a = V3 a a a deriving (Eq, Show)

instance Functor V3 where
    fmap f (V3 a b c) = V3 (f a) (f b) (f c)

instance (Num a) => Vector (V3 a) where
    type Item (V3 a) = a
    (V3 a b c) `vplus` (V3 d e f) = V3 (a + d) (b + e) (c + f)
    vnegate v = fmap negate v
    dot (V3 a b c) (V3 d e f) = a*d + b*e + c*f

这有助于类型检查器,特别是它意味着您不需要上面的显式类型签名。这也意味着您的推断类型不会(a, a, a)而是V3 a(就像您看到[Char]而非String时),这更容易理解。它并不重要,但很有帮助。

如果您想知道,GHC.Exts.IsList(与新的OverloadedLists扩展程序一起使用)是这样的:

class IsList l where
    type family GHC.Exts.Item l :: *
    fromList :: [GHC.Exts.Item l] -> l
    fromListN :: Int -> [GHC.Exts.Item l] -> l
    toList :: l -> [GHC.Exts.Item l]