我正在努力学习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]
答案 0 :(得分:9)
问题在于您的类型签名和可怕的单态限制。您的第一个版本中有类型签名,但在第二个版本中没有;具有讽刺意味的是,它会反过来发挥作用!
试试这个:
λ>let t :: Eq x => [x] -> Int; t = length . nub
λ>t [1]
1
单态限制强制看起来不像函数的东西具有单态类型,除非它们具有显式类型签名。您想要 t
的类型是多态的:请注意类型变量x
。但是,对于单态限制,x
被“默认”为()
。看看这个:
λ>let t = length . nub
λ>:t t
t :: [()] -> Int
这与上面带有类型签名的版本非常不同!
由于defaulting,编译器为单态类型选择()
。默认只是Haskell用于从类型类中选择类型的过程。所有这一切的意思是,在repl中,如果遇到()
,Show
或Eq
类中的模糊类型变量,Haskell将尝试使用Ord
类型。是的,这基本上是随意的,但是无需在任何地方编写类型签名,这对于玩游戏非常方便!此外,默认规则在文件中更为保守,因此这基本上只是在GHCi中发生的事情。
事实上,默认为()
似乎主要是让printf
在GHCi中正常工作的黑客攻击!这是一个不起眼的Haskell古玩,但在实践中我会忽略它。
除了包含类型签名外,您还可以在repl中关闭单态限制:
λ>:set -XNoMonomorphismRestriction
这在GHCi中很好,但我不会在实际模块中使用它 - 相反,请确保始终在文件中包含顶级定义的类型签名。
编辑:自GHC 7.8.1起,GHCi默认关闭单态限制。这意味着所有这些代码都可以与最新版本的GHCi一起使用,并且您不需要显式设置该标志。但是,对于没有类型签名的文件中定义的值,它仍然是一个问题。
答案 1 :(得分:1)
这是“Dreaded”Monomorphism Restriction的另一个例子,它导致GHCi推断出组合函数的单态类型。您可以使用
在GHCi中禁用它> :set -XNoMonomorphismRestriction