2013-12-09 77 views
3

我在学习哈斯克尔,我一直在回顾第6章和第7章学习哈斯克尔。为什么下面两个函数定义没有给出相同的结果?我认为(f。g)x = f(g(x))?哈斯克尔函数组合混淆

防守1

let{ t :: Eq x => [x] -> Int; t xs = length(nub xs)} 
t [1] 
1 

防守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] 

回答

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中,如果Haskell在Show,EqOrd类中遇到模糊类型变量,它将尝试使用()类型。是的,这基本上是任意的,但是无需在任何地方写入类型签名就可以轻松玩游戏!而且,违约规则在文件中更加保守,所以这基本上就是GHCi中发生的事情。

事实上,默认为()似乎主要是一个破解,使printf在GHCi中正常工作!这是一个晦涩的Haskell古玩,但我会在实践中忽略它。

除了包括类型签名,你也只是把单态限制关闭在REPL:

λ>:set -XNoMonomorphismRestriction 

这是GHCI很好,但我不会在现实模块使用它 - 相反,确保始终在文件中包含顶级定义的类型签名。

编辑:自从GHC 7.8.1以来,GHCi中默认关闭了单态限制。这意味着所有这些代码都可以在GHCi的最新版本中正常工作,并且不需要明确设置标志。但是,对于没有类型签名的文件中定义的值来说,它仍然是个问题。

+6

我在什么时候它变得不可能指的是单态的限制,而不在前面加上形容词“可怕的” :) –

+3

@ChrisTaylor想禁用它:对于这一现象的技术术语是“epitheton奥尔南”。 –

1

这是“Dreaded”Monomorphism Restriction的另一个实例,它导致GHCi推断组合函数的单形类型。您可以在GHCI与

> :set -XNoMonomorphismRestriction