在Haskell中键入签名

时间:2014-06-18 16:54:04

标签: haskell

我的问题是关于类型签名。

以下代码符合:

data Vector a = Vector a a a deriving (Show) 
vMult :: (Num a) => Vector a -> a -> Vector a 
(Vector i j k) `vMult` m = Vector (i*m) (j*m) (k*m)

但是,我不明白为什么用以下 代替上述类型签名(第2行) 工作:

vMult :: (Num a) => Vector a -> Num -> Vector a  

我的理解是,m属于Num类型(例如,数字8),而且i, j, k也是Num,因此应该没有问题计算Vector (i*m) (j*m) (k*m)

请理解我的理解。

3 个答案:

答案 0 :(得分:6)

Num a实际上并不是一种类型

Num是一个类型类,所以如果我说Num a,则表示a是数字类型,那么

vMult :: (Num a) => Vector a -> a -> Vector a

表示"只要a是数字类型,vMult采用a s和a的向量,并返回{的向量{1}} S"

这意味着a可以像
一样工作 vMult
vMult :: Vector Int -> Int -> Vector Int
请注意,您可以通过使用单个数字类型替换原始文件中的所有vMult :: Vector Double -> Double -> Vector Double来获取每种类型。

a对它自己没有意义

Num本身意味着"是数字类型",所以如果你的函数有类型签名

Num

它会读取"只要vMult :: (Num a) => Vector a -> Num -> Vector a 是数字类型,a采用vMult s的向量而a是数字类型并返回Vector of { {1}} S&#34。它的英语不合语法就像Haskell中的错误一样。

这就像说'#34;给我一些黄油,并且有一个尖锐的优势"而不是"给我一些黄油和一把刀"。有效的类型签名就像是说"找到你可以传播的东西(称之为k),并给我一些黄油和k。"

事实不属于

您也不能使用a代替a Num a,因为您无法在需要名词的位置使用断言:"给我是你的钥匙圈,钥匙是用金属制成的,所以我可以给你一个新的钥匙圈"。

答案 1 :(得分:2)

乘法是在Num aNum a上定义的,即同一类型的2个参数属于Num类型类。

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

如果您的类型签名中只有Num,则类似于要求允许在IntegerFloat之间进行乘法,因为两者都是属于类型类的类型Num。这就是为什么你需要指定应该使用的类型而不仅仅是Typeclass

当您将a用于Num时,已确定Vector应属于Vector。为了将Num中的每个元素与所选标量相乘,该标量不仅必须属于a,还必须属于同一类型,即{{1}}。

答案 2 :(得分:2)

Num不是可以在那里使用的concrete type。它是一元type constructor,必须专门使用另一种类型。用OOP术语来说,Num不是一个类似OOP的抽象“超类”类型,而是一个必须专门用于它的工厂。

因此,尝试将Num传递到需要kind(类型的“类型”)*的地方就像尝试传递函数一样(:: a -> b)其中需要一个值(:: c)(不同之处在于前者是在类型级别完成的):

ghci> :kind Int
Int :: *

ghci> :kind Num
Num :: * -> Constraint

因此,我们可以看到Num在类型检查时将*应用于任何类型Constraint会产生种类Constraint,而=>只能在a之前使用Vector a -> a -> Vector a类型签名中的{1}}(限制Constraint中{{1}}位置的正确类型,{{1}}的含义。