【问题标题】:Why Doesn't Haskell Generalize Some Functions为什么 Haskell 不泛化一些函数
【发布时间】:2013-10-09 02:54:00
【问题描述】:

我在一个文件中有以下内容:

import Control.Monad
ema a = scanl1 $ \m n -> (1-a)*m + a*n
macd  = ema 9 . uncurry (zipWith (-)) . liftM2 (,) (ema 26) (ema 12)

在编译时,我得到以下信息:

:t macd
macd :: [Integer] -> [Integer]

然而,

:t ema 9 . uncurry (zipWith (-)) . liftM2 (,) (ema 26) (ema 12)
ema 9 . uncurry (zipWith (-)) . liftM2 (,) (ema 26) (ema 12)
  :: Num a => [a] -> [a]

那么,为什么macd 的限制类型不同?

【问题讨论】:

    标签: haskell types


    【解决方案1】:

    这是单态限制。

    要点是,当您有一个受约束的类型变量时,如果它绑定到单个标识符,Haskell 将不会泛化

    f = term
    

    但是如果是函数绑定,例如

    f a ... = term
    

    然后是泛化的。我已经回答了这个问题,所以我在blog post 中写了一个更完整的例子


    至于为什么我们有单态限制,

    -- let's say comp has the type [Num a => a]
    foo = (comp, comp)
      where comp = super_expensive_computation
    

    comp 将被计算多少次?如果我们自动推断一般类型,它可以计算两次。但是,如果您编写类似这样的内容打算使用 Num a => (a, a) 或类似类型,这可能会让您感到惊讶。

    发生额外的计算是因为在 Core 领域类似

    foo :: Num a => a
    

    变成更像

     foo :: NumDict -> a -- NumDict has the appropriate functions for + - etc
                         -- for our a
    

    一个函数。由于foos 的一般类型是(Num a, Num b) => (a, b),除非GHC 可以证明comp 在两种情况下得到的NumDicts 是相同的,它不能共享comp 的结果

    【讨论】:

    • 作为一个元注释,确实应该有一个明确的“这就是单态限制是什么”问题,我们可以在这里链接到
    • 我明白了。谢谢。解决此问题的简单方法是 ema a xs = scanl1 (\m n -> (1-a)*m + a*n) xsmacd xs = ema 9 . uncurry (zipWith (-)) . liftM2 (,) (ema 26) (ema 12) $ xs
    • @me2 是的,或者只是添加类型签名:) 通常这是我推荐的。它对人类和编译器都有帮助
    • @me2 Jozefg 提到 Num a => a 被转为 NumDict -> a,如果你想确切地知道它是如何实现的,我建议这个精彩的演讲 channel9.msdn.com/posts/…。它非常平易近人,您不需要太多的 haskell 知识就可以学习,到最后您会发现类型类实际上只是纯数据类型的漂亮语法。
    猜你喜欢
    • 2016-09-30
    • 1970-01-01
    • 1970-01-01
    • 2014-04-30
    • 2013-07-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多