例如:
map (+1) 2
以ghci产量计
<interactive>:23:1: error:
* Non type-variable argument in the constraint: Num [b]
(Use FlexibleContexts to permit this)
* When checking the inferred type
it :: forall b. (Num b, Num [b]) => [b]
我见过许多类似的问题,但似乎都只是回答我们由此可以推断出什么(map
的第二个参数的类型是错误的),以及如何修复它--而不是错误实际 * 意味着 * 什么。
3条答案
按热度按时间km0tfn4u1#
错误是在你的陈述式的类型推导过程中出现的。
自
(+1)
的类型为Num a => a -> a
2
的类型为Num a => a
map
的类型为(a -> b) -> [a] -> [b]
我们知道
map (+1)
的类型必须是(Num b) => [b] -> [b]
,因此map (+1) 2
的类型必须是(Num b, Num [b]) => [b]
,但是[b]
不仅仅是一个类型变量,它是某个类型变量的 List,其中list是一个 * 数据构造函数 *,在Haskell的另一个版本中,没有列表的语法糖存在,我们可以写(Num b, Num (List b))
。这是一个问题,因为默认情况下,Haskell不支持 * 非类型变量参数的约束 *。因此,问题的确切本质不是Haskell不知道如何Map数字-而是它不允许我们的函数调用产生的类型的值。
但这条规则并不是严格需要的。通过在调用ghci时添加
-XFlexibleContexts
,我们的方法产生的类型现在被允许了。原因是Haskell中的文字2
并不真正表示数字-它表示Num a => a
类型的对象。它是用fromIntegral
从积分2
构造的。所以语句map (+1) 2
等价于map (+1) (fromIntegral (2::Integer))
。这意味着文字“2”可以表示任何东西,只要给出适当的示例化-包括列表。weylhg0b2#
2
具有类型Num a => a
;我们没有指定a
* 是什么 *,只是它必须有一个Num
示例。map (+1)
具有类型Num b => [b] -> [b]
;我们已经指定了b
是什么,只是它必须有一个Num
示例。当我们确定
map (+1) 2
的类型时,我们基本上统一了Num a ~ Num b => [b]
。这就是问题所在。
Num
需要a
或b
之类的类型变量作为其参数,而不是[b]
之类的多态类型。x33g5p2x3#
以下是实际答案:
GHC试图自己推断类型,但失败了,或者用GHC的行话来说:
基本上,冲突是:
· GHC很乐意将任何值作为
map (+1)
的参数,只要它与map (+1)
的类型兼容,而map (+1)
需要一个数字列表,因为+
需要两个数字,而1
已经是一个数字,而map
总是需要一个与之兼容的值列表。·关键在于:**GHC很乐意让
2
为任何类型!**因为为了保存总是必须指定文字2
是否为特定类型的麻烦,GHC只将数字文字视为Integer
,并将其替换为fromInteger 2
。因此,由于fromInteger
来自Num
类,2
可以是实现类Num
的任何内容!因此2
也可以是列表!所以GHC一直在尝试将这些东西组合在一起,并告诉你:
或者在 haskell :
但没有示例,使列表数字!
或者用GHC的行话来说:
这就是为什么你现在得到这个,是的,肯定是设计糟糕,令人困惑的错误信息.
(几乎所有的GHC错误信息都是一个残酷的笑话。要理解它们,最好的办法就是 * 从下到上 * 阅读它们!了解所有Haskell的类型系统自由和GHC扩展,这些扩展迫使GHC不要做出假设,否则这些错误将成为更简单的错误子类。)
**很明显,**你通常只需要用一个实际的列表来替换
2
。但是我们在这里不会这样做。既然GHC想要疯狂,GHC就应该疯狂:愚蠢的决议
因此,既然它给我们留下了制作数字列表的选项,那就是我们要做的:
所以现在,它运行得非常好:
因为
2
变成了fromInteger 2 :: Num a => [a]
,它把2
放到一个列表([2]
)中,然后map (+1)
很高兴,把它变成了[2+1]
,它的求值结果是[3]
。**这种可能性就是 * 为什么 * 错误消息不是简单得多
2
不是一个列表,但map
需要一个列表!"*.**