我一直在阅读“了解你一个Haskell”这本书,我正试图围绕Haskell Type Classes。作为练习,我正在尝试创建一个简单的矢量类型类。以下代码片段给了我一些悲伤(导致我第一篇发布到StackOverflow):
data Vec2 a = Vec2 (a,a) deriving (Show, Eq, Read)
class Vector a where
(<*) :: (Num b) => a -> b -> a
instance (Num a) => Vector (Vec2 a) where
Vec2 (x,y) <* a = Vec2 (a*x, a*y)
我收到以下错误消息:
Could not deduce (a~b) from the context (Num a) or from (Num b) bound by the type signature for
<* :: Num b => Vec2 a -> b -> Vec2 a
似乎在类型类中指定的Num
应提供a
的类型,并且实例中的Num a
注释应提供x
的类型和{ {1}},为什么抱怨?我对这段代码有什么误解?
答案 0 :(得分:7)
(*) :: Num a => a -> a -> a
的类型。但是,当您实际尝试使用*
时,实际上是将两个不相关的类型与Num
个实例相乘,并且编译器无法推断它们是相同的。
要更清楚地解释一下<*
的类型和b
的普遍量化
(<*) :: (Num b) => a -> b -> a
你在这里说的是,给我任何具有Num
实例的类型,我将能够将其与我的向量相乘,但你想说的是不同的东西。
您需要了解类型a
中的Vec2 a
与b
类型中的(<*) :: Num b => a -> b -> a
相同的情况,然后才能将它们相乘。这是一个使用typefamilies来确保这种约束的解决方案。
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FlexibleContexts #-}
data Vec2 a = Vec2 (a,a) deriving (Show, Eq, Read)
class (Num (VectorOf a)) => Vector a where
type VectorOf a :: *
(<*) :: a -> (VectorOf a) -> a
instance (Num a) => Vector (Vec2 a) where
type VectorOf (Vec2 a) = a
Vec2 (x,y) <* a = Vec2 (a*x, a*y)
答案 1 :(得分:0)
编译器无法验证所涉及的Num
个实例是否实际上是同一类型。确实,它们都是Num
个实例,但还需要的是它们必须是相同的实例。
否则,你可以这样写:
Vec2 (1 :: Double, 2 :: Double) <* (3 :: Int)
当时间到来时不会飞,例如:(1 :: Double) * (3 :: Int)
。
答案 2 :(得分:0)
我认为问题是(*)
类型(Num a) => a -> a -> a
而非(Num a, Num b) => a -> b -> a
编译器预期。
我不熟悉Haskell的数字转换,但我的解决方法有限,因为第二个参数是Integral
的实例。
data Vec2 a = Vec2 (a,a) deriving (Show, Eq, Read)
class Vector a where
(<*) :: (Integral b) => a -> b -> a
instance (Num a) => Vector (Vec2 a) where
Vec2 (x,y) <* a = Vec2 (x*b, y*b)
where b = fromIntegral a
因为fromIntegral
的类型为(Integral a, Num b) => a -> b
,所以可以根据需要转换*
的第二个参数。