21

它是否有助于编译器进行优化,或者只是添加额外的类型签名的多余工作?例如,人们经常看到:

foo :: a -> b
foo x = bar x
      where bar x = undefined

而不是:

foo :: a -> b
foo x = bar x
      where bar :: a -> b
            bar x = undefined

如果我省略了顶级类型签名,GHC 会给我一个警告,所以如果我没有收到警告,我很有信心我的程序是正确的。但是,如果我在 where 子句中省略签名,则不会发出警告。

4

4 回答 4

22

存在一类本地函数,其类型不能用 Haskell 编写(即不使用花哨的 GHC 扩展)。例如:

f :: a -> (a, Int)
f h = g 1
  where g n = (h, n)

这是因为从外部看ainf类型签名是多态的f,但从内部看却不是这样f。在g中,它只是一些未知类型,而不是任何类型,并且(标准)Haskell 无法用其类型语言表达“与定义此函数的函数的第一个参数相同的类型”。

于 2012-05-15T23:18:55.580 回答
20

where如果子表达式在定义中多次出现,则子句中的定义通常是为了避免重复自己。在这种情况下,程序员将局部定义视为写出内联子表达式的简单替代。您通常不会显式键入内联子表达式,因此您也不会键入where定义。如果你这样做是为了节省打字,那么类型声明会扼杀你所有的积蓄。

向 Haskell 学习者介绍这种形式的示例似乎很常见where,因此他们继续认为“正常样式”是不为局部定义提供类型声明。至少,那是我学习 Haskell 的经验。where从那以后,我发现如果我不知道本地定义的类型,我的许多复杂到需要一个块的函数变得相当难以理解,所以我试图错误地现在总是输入它们;即使我在编写代码时认为类型很明显,但当我有一段时间没有看它后阅读它时,它可能并不那么明显。在我的脑海中运行类型推断的一两个实例几乎总是超过我手指的一点努力!

Ingo 的回答给出了故意不为本地定义提供类型的充分理由,但我怀疑主要原因是许多程序员已经吸收了经验法则,即为顶级定义提供类型声明,而不是为本地定义提供类型声明他们学习了 Haskell。

于 2012-05-16T01:05:10.787 回答
11

声明通常where用于简短的本地事物,它们具有简单的类型或易于推断的类型。结果,添加类型对人类或编译器没有任何好处。

如果类型很复杂,或者无法推断,那么您可能需要添加类型。

虽然提供单态类型签名可以使顶级函数更快,但对于where子句中的本地定义来说并不是一个胜利,因为 GHC 无论如何都会内联和优化定义。

于 2012-05-15T23:07:46.020 回答
0

添加类型签名可以使您的代码更快。以下面的程序(斐波那契)为例:

result = fib 25 ;
-- fib :: Int -> Int
fib x = if x<2 then 1 else (fib (x-1)) + (fib (x-2))
  • 如果没有第 2 行中的注释,则需要 0.010 秒。跑步。
  • 使用Int -> Int注释,需要 0.002 秒。

发生这种情况是因为如果你什么都不说fib,它将被键入为fib :: (Num a, Num a1, Ord a) => a -> a1,这意味着在运行时,必须在函数之间传递额外的数据结构(“字典”)来表示Num/Ord类型类。

于 2012-05-15T23:07:16.127 回答