我正在努力学习haskell,而且我一直在学习了解你是一个Haskell的第6章和第7章.为什么以下两个函数定义不能给出相同的结果?我以为(f.g)x = f(g(x))?
Def 1
let{ t :: Eq x => [x] -> Int; t xs = length( nub xs)} t [1] 1
Def 2
let t = length . nub t [1]:78:4: No instance for (Num ()) arising from the literal `1' Possible fix: add an instance declaration for (Num ()) In the expression: 1 In the first argument of `t', namely `[1]' In the expression: t [1]
Tikhon Jelvi.. 9
问题在于您的类型签名和可怕的单态限制.您的第一个版本中有类型签名,但在第二个版本中没有; 具有讽刺意味的是,它本来会有相反的方式!
试试这个:
?>let t :: Eq x => [x] -> Int; t = length . nub ?>t [1] 1
单态限制强制看起来不像函数的东西具有单态类型,除非它们具有显式类型签名.您的类型要对t
具有多态性:注意类型变量x
.然而,随着单态限制,x
被"默认"为()
.看一下这个:
?>let t = length . nub ?>:t t t :: [()] -> Int
这与上面带有类型签名的版本非常不同!
()
由于默认,编译器选择单态类型.默认只是Haskell用于从类型类中选择类型的过程.所有这一切的真正含义是,在REPL,哈斯克尔会尝试使用()
,如果在遇到模棱两可的类型变量类型Show
,Eq
或Ord
类.是的,这基本上是随意的,但是无需在任何地方编写类型签名,这对于玩游戏非常方便!此外,默认规则在文件中更为保守,因此这基本上只是在GHCi中发生的事情.
事实上,默认()
似乎主要是printf
在GHCi中正确工作的黑客!这是一个不起眼的Haskell古玩,但我在实践中忽略了它.
除了包含类型签名外,您还可以在repl中关闭单态限制:
?>:set -XNoMonomorphismRestriction
这在GHCi中很好,但我不会在实际模块中使用它 - 相反,请确保始终在文件内包含顶级定义的类型签名.
编辑:自GHC 7.8.1起,GHCi默认关闭单态限制.这意味着所有这些代码都可以与最新版本的GHCi一起使用,并且您不需要显式设置标志.但是,对于没有类型签名的文件中定义的值,它仍然是一个问题.