【问题标题】:Haskell function composition confusionHaskell 函数组合混淆
【发布时间】:2013-12-26 13:34:46
【问题描述】:

我正在尝试学习 Haskell,我已经阅读了 Learn you a 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]

【问题讨论】:

    标签: haskell


    【解决方案1】:

    问题在于您的类型签名和可怕的单态限制。您在第一个版本中有类型签名,但在第二个版本中没有;具有讽刺意味的是,它会以相反的方式工作!

    试试这个:

    λ>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 中,如果在 ShowEqOrd 类中遇到不明确的类型变量,Haskell 将尝试使用 () 类型。是的,这基本上是任意的,但它非常方便玩,无需到处写类型签名!另外,默认规则在文件中比较保守,所以这基本上只是在 GHCi 中发生的事情。

    事实上,默认为() 似乎主要是为了让printf 在GHCi 中正常工作!这是一个不起眼的 Haskell 古玩,但在实践中我会忽略它。

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

    λ>:set -XNoMonomorphismRestriction
    

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

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

    【讨论】:

    • 我想知道在什么情况下,如果不加形容词“可怕”,就不可能引用单态限制:)
    • @ChrisTaylor:这种现象的技术术语是“epitheton ornans”。
    【解决方案2】:

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

    > :set -XNoMonomorphismRestriction
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-26
      • 2016-04-30
      • 2010-12-01
      • 1970-01-01
      • 1970-01-01
      • 2013-10-11
      相关资源
      最近更新 更多