3

我一直在浏览“Learn You a Haskell”一书,我正试图围绕 Haskell 类型类。作为实践,我正在尝试创建一个简单的矢量类型类。下面的代码片段让我有些悲伤(导致我在 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

似乎typeclass 中Num指定的内容应该提供 of 的类型a,而Num a实例中的规范应该提供 and 的类型xy那么为什么会抱怨呢?我对这段代码有什么误解?

4

3 回答 3

7

的类型(*) :: Num a => a -> a -> a。但是,当您实际尝试使用 时*,实际上是在将两个具有实例的不相关类型相乘,Num并且编译器无法推断它们是相同的。

为了更清楚地解释它,请查看 的类型<*和普遍量化b

(<*) :: (Num b) => a -> b -> a

你在这里说的是,给我任何有Num实例的类型,我就可以将它与我的向量相乘,但你想说的是不同的东西。

您需要一些方法说type 中的typea与 typeVec2 a中的相同,然后才能将它们相乘。这是使用类型族来确保此约束的解决方案。b(<*) :: Num b => a -> b -> a

{-# 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)
于 2013-08-17T04:47:51.190 回答
0

编译器无法验证所Num涉及的两个实例实际上是同一类型。当然,它们都是Num实例,但还需要它们必须是相同的实例。

否则,您可以编写如下内容:

Vec2 (1 :: Double, 2 :: Double) <* (3 :: Int)

当需要做的时候它不会飞,例如:(1 :: Double) * (3 :: Int).

于 2013-08-17T04:39:53.920 回答
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有 type (Integral a, Num b) => a -> b,它可以根据需要转换 的第二个参数*

于 2013-08-17T04:56:24.137 回答