从当前签名或其他函数返回类型获取时,Haskell类型不会被视为相同

时间:2013-10-30 10:06:50

标签: haskell types

我开始按照this answer中的建议学习Haskell。 所以我只是实现了简单列表函数,我偶然发现编译器行为的差异,我无法向自己解释:

-- Impl 1
elementAt :: (Integral b) => [a] -> b -> a
elementAt xs id = xs !! (fromIntegral(id-1))

-- Impl 2
elementAt' :: (Num b) => [a] -> b -> a
elementAt' xs id = xs !! (id-1)

以下签名:

fromIntegral :: (Integral a, Num b) => a -> b
(!!) :: [a] -> Int -> a

我仅在第二次实施时遇到错误elementAt'

Could not deduce (b ~ Int)
from the context (Num b)

如果我理解正确,这意味着运算符(!!)期望 Int 实例作为其第二个参数(从签名中看到),但我们只保证提供的参数是符合 Num 类型类(从elemenAt'签名推断),它比 Int 更宽。

考虑到这一点,我不明白为什么第一个实现确实有效,因为知道fromIntegral也返回一个只符合 Num 类型类的值。

1 个答案:

答案 0 :(得分:5)

fromIntegral返回 Num类的任何实例。即,无论你需要什么实例,它都会知道如何制作它。这是一个类型变量的想法,它基本上是一个额外的编译时参数,函数的调用者可以选择它。这就是为什么elemAt有效:编译器知道我们需要Int,因此它会告诉fromIntegral,然后知道该怎么做。

但是,如果定义了一个带有签名Num b => ...的函数,你还需要允许调用者为b添加任何类型的选择,前提是它是在Num课程中。在这种情况下,你要求提供特定的实例Int,但需要接受调用者给你的任何内容。这就是区别。

实际上,Num b => [a] -> b -> a不是您可以有效定义此功能的签名。你如何用复数,甚至是无限维矩阵或其他什么来索引列表?你可以做的是Integral b => [a] -> b -> a