2013-08-17 72 views
3

我一直在通过“学习你一个Haskell”书,我试图包裹我的头哈斯克尔类型类。作为练习,我试图创建一个简单的矢量类型的类。下面的代码片段一直给我一些悲伤(导致我的第一篇到StackOverflow上):Haskell Typeclass类型限制和扣除

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正在指定类型类型应该提供aNum a类型的说明,该类型应提供xy的类型,那么它为什么会抱怨?我对这段代码有什么误解?

+0

提示,b是普遍量化的。 – augustss

+0

提示,'(*):: Num a => a - > a - > a',因为b是通用量化的,所以编译器在使用'*'时不能推导出'a〜b'。 – Satvik

回答

7

(*) :: Num a => a -> a -> a的类型。但是当你真的试图使用*时,你实际上会乘以两个具有Num实例的无关类型,编译器无法推断它们是相同的。

为了解释得更清楚看看<*类型和你说的这是什么的b

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

普遍定量,给我有Num例如任何类型和我将能够乘上与我的矢量,但你想说的是不同的东西。

你需要一些如何说a在类型Vec2 a是相同类型(<*) :: Num b => a -> b -> ab,只有这样,你可以乘在一起。这是一个使用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) 
0

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

否则,你可以写这样的事情:

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

当谈到时间做不靠谱,例如:(1 :: Double) * (3 :: Int)

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,根据需要它可以转换的*第二个参数。

+0

用“4.2”表示乘法怎么样? – Satvik

+0

@Satvik它只适用于Vec2与积分倍增,我想有''(Num a,Num b)=> a - > b - > a'类型的函数来代替'*',但我没有想法如何实现它。 – Javran