为什么Haskell在没有单态性限制的情况下无法避免重复评估?


14

前几天,我刚学完了youahaskell,并且试图理解Haskell Wiki中描述的“单态限制” 。我想我了解MR如何防止重复评估,但是我看不出为什么用更直接的方法无法避免重复评估。

我想到的具体示例是Wiki使用的示例:

f xs = (len,len)
  where
    len = genericLength xs

哪里genericLength是类型Num a => [b] -> a

显然,genericLength xs只需计算一次即可求值(len,len),因为它是具有相同参数的相同函数。我们不需要f知道任何调用即可知道这一点。那么,为什么Haskell不能在不引入MR之类的规则的情况下进行此优化?

那个Wiki页面上的讨论告诉我,它与Num类型类而不是具体类型有关,但是即使这样,在编译时纯函数是否返回相同的值也不应该很明显- -并因此使用相同的Num具体类型-两次给相同的参数?

Answers:


11

它是相同的函数名称,具有相同的参数,但可能具有不同的返回类型和实现,因为它是多态的。这意味着,如果它被称为在上下文期待的(Int, MyWeirdCustomNumType)返回类型,它评估了两次,因为实行(+)Int是实行完全不同(+)MyWeirdCustomNumType。您需要在某些时候运行物理上不同的代码。

这就是为什么Num类型类很重要。这意味着您针对不同类型具有不同的实现。这也意味着如果f在库中,则在编译时不知道它可能需要返回的所有类型组合。

因此,是的,您确实需要查看的调用f才能知道要使用的返回类型。在大多数情况下,您会希望它们相同,这就是为什么它们默认情况下将单态限制置于其中的原因。可以在极少数情况下将其关闭。在实践中,程序员无论如何都不倾向于将这种情况留给类型推断。


我没有考虑过的可能性f [] :: (Int, Float)。现在,它变得非常合理。谢谢。
Ixrec 2015年

1
It's the same function name, with the same arguments, but potentially different return types and implementations, because it's polymorphic.我认为更好的查看方法是,类型类实例实际上是额外的参数,genericLength并且编译器不认为这两个调用中的参数相同。
2015年

快速的侧面问题。如果MonomorphismRestriction已关闭,但是稍后您执行了类似的操作a = uncurry (==) $ f [1, 2, 3],是否可以优化该呼叫站点以仅检查[1, 2, 3]一次长度?如果是这样的话,那么我对真正的单态性限制实际上是在为您买单感到困惑,如果不是,那为什么不呢?
分号
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.