Haskell函数组合混乱



我正在努力学习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在ShowEqOrd类中遇到不明确的类型变量,它将尝试使用()类型。是的,这基本上是任意的,但它非常方便,不必到处写类型签名!此外,默认规则在文件中更为保守,所以这基本上只是GHCi中发生的事情。

事实上,默认为()似乎主要是为了让printf在GHCi中正确工作!这是一件晦涩难懂的哈斯克尔古玩,但在实践中我会忽略它。

除了包括类型签名外,您还可以在repl:中关闭单态性限制

λ>:set -XNoMonomorphismRestriction

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

编辑:自GHC 7.8.1以来,GHCi中的单态性限制默认关闭。这意味着所有这些代码都可以与最新版本的GHCi配合使用,并且不需要显式设置标志。然而,对于在没有类型签名的文件中定义的值来说,这仍然是一个问题。

这是"Dreaded"单态性限制的另一个实例,它导致GHCi推断组合函数的单态类型。你可以用在GHCi中禁用它

> :set -XNoMonomorphismRestriction

最新更新