它是否有助于编译器进行优化,或者仅仅是添加其他类型签名的剩余工作?例如,经常会看到:
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子句中的签名,则不会发出警告。
答案 0 :(得分:20)
存在一类本地函数,其类型不能用Haskell编写(不使用花哨的GHC扩展,即)。例如:
f :: a -> (a, Int)
f h = g 1
where g n = (h, n)
这是因为虽然a
类型签名中的f
是从外部f
查看的多态,但f
内的情况并非如此。在g
中,它只是某些未知类型,但不是任何类型,而(标准)Haskell无法表达“与第一个参数相同的类型函数这个函数在“它的类型语言中定义。
答案 1 :(得分:18)
where
子句中的定义通常是为了避免在定义中多次出现子表达式时重复自己。在这种情况下,程序员将本地定义视为用于写出内联子表达式的简单替代。您通常不会显式键入内联子表达式,因此您也不要键入where
定义。如果你这样做是为了节省打字,那么类型声明会杀死你所有的积蓄。
向Haskell的学习者介绍where
这个形式的例子似乎很常见,所以他们继续认为“正常风格”不是为本地定义提供类型声明。至少,这是我学习Haskell的经验。我已经发现,如果我不知道本地定义的类型,我的许多复杂到足以需要where
块的函数变得相当难以理解,所以我试图错误地总是输入它们;即使我在编写代码时我认为类型很明显,但是在我看了一段时间之后阅读它时可能并不那么明显。即使在我头脑中进行类型推断的一两个实例,我的手指也会有点费力!
Ingo的答案给出了一个很好的理由故意没有给出一个本地定义的类型,但我怀疑主要原因是许多程序员已经同化了经验法则为顶部提供了类型声明级别定义,但不是他们学习Haskell的方式的本地定义。
答案 2 :(得分:11)
通常where
声明用于简短的本地事物,它们具有简单的类型或易于推断的类型。因此,人类或编译器添加类型没有任何好处。
如果类型很复杂,或者无法推断,那么您可能需要添加类型。
虽然提供单态类型签名可以使顶级函数更快,但在where
子句中对局部定义的胜利并非如此,因为GHC无论如何都会在大多数情况下内联和优化定义。 / p>
答案 3 :(得分:0)
添加类型签名可以使您的代码更快。以下面的程序(Fibonacci)为例:
result = fib 25 ;
-- fib :: Int -> Int
fib x = if x<2 then 1 else (fib (x-1)) + (fib (x-2))
Int -> Int
注释,需要0.002秒。这是因为如果你没有对fib
说什么,它将被输入为fib :: (Num a, Num a1, Ord a) => a -> a1
,这意味着在运行时,额外的数据结构(“词典”)将不得不在函数之间传递以表示Num
/ Ord
类型类。