在类型 class 中使用实例类型参数
Use instance type parameter in type class
作为练习,我正在尝试创建一个 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
但是,我似乎无法从 Vector
中访问类型参数 a
,所以我无法让 dot
操作 Vector
我想要的方式。我将如何访问 a
类型参数?
您想为此使用 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]
另一种不需要扩展的解决方案是使用更高种类的 class。因此:
class Vector v where
vplus :: Num a => v a -> v a -> v a
vnegate :: Num a => v a -> v a
然后加个点积的方法就很简单了:
dot :: Num a => v a -> v a -> a
实例方法实现不必更改,但实例声明本身必须更改:
instance Vector V3 where
-- method implementations are the same as before
作为练习,我正在尝试创建一个 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
但是,我似乎无法从 Vector
中访问类型参数 a
,所以我无法让 dot
操作 Vector
我想要的方式。我将如何访问 a
类型参数?
您想为此使用 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]
另一种不需要扩展的解决方案是使用更高种类的 class。因此:
class Vector v where
vplus :: Num a => v a -> v a -> v a
vnegate :: Num a => v a -> v a
然后加个点积的方法就很简单了:
dot :: Num a => v a -> v a -> a
实例方法实现不必更改,但实例声明本身必须更改:
instance Vector V3 where
-- method implementations are the same as before