我正在努力学习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]
<interactive>: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]
问题在于您的类型签名和可怕的单态限制。您在第一个版本中有一个类型签名,但在第二个版本中没有;具有讽刺意味的是,它本来会反过来起作用的!
试试这个:
λ>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中,如果Haskell在Show
、Eq
或Ord
类中遇到不明确的类型变量,它将尝试使用()
类型。是的,这基本上是任意的,但它非常方便,不必到处写类型签名!此外,默认规则在文件中更为保守,所以这基本上只是GHCi中发生的事情。
事实上,默认为()
似乎主要是为了让printf
在GHCi中正确工作!这是一件晦涩难懂的哈斯克尔古玩,但在实践中我会忽略它。
除了包括类型签名外,您还可以在repl:中关闭单态性限制
λ>:set -XNoMonomorphismRestriction
这在GHCi中很好,但我不会在实际模块中使用它——相反,请确保始终在文件中包含顶级定义的类型签名。
编辑:自GHC 7.8.1以来,GHCi中的单态性限制默认关闭。这意味着所有这些代码都可以与最新版本的GHCi配合使用,并且不需要显式设置标志。然而,对于在没有类型签名的文件中定义的值来说,这仍然是一个问题。
这是"Dreaded"单态性限制的另一个实例,它导致GHCi推断组合函数的单态类型。你可以用在GHCi中禁用它
> :set -XNoMonomorphismRestriction