为什么在where子句中使用类型签名如此罕见?

eps*_*lbe 20 haskell where-clause type-signature

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

foo :: a -> b
foo x = bar x
      where bar x = undefined
Run Code Online (Sandbox Code Playgroud)

而不是:

foo :: a -> b
foo x = bar x
      where bar :: a -> b
            bar x = undefined
Run Code Online (Sandbox Code Playgroud)

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

Ing*_*ngo 21

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

f :: a -> (a, Int)
f h = g 1
  where g n = (h, n)
Run Code Online (Sandbox Code Playgroud)

这是因为虽然af类型签名是多态的,从外面看f,这也不是那么从内部f.在g,它只是一些未知类型,但不是任何类型,并且(标准)Haskell不能在其类型语言中表达"与函数的第一个参数相同的类型".

  • 虽然`{ - #LANGUAGE ScopedTypeVariables# - }`允许你给`g :: Int - >(a,Int)`类型,如果你修改`f`的类型签名是`forall a.a - >(a,Int)`. (6认同)

Ben*_*Ben 18

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

where向Haskell的学习者介绍这种形式的例子似乎很常见,因此他们继续认为"正常风格"不是为本地定义提供类型声明.至少,这是我学习Haskell的经验.我已经发现,where如果我不知道本地定义的类型,那么复杂到足以需要块的许多函数变得相当难以理解,所以我试图总是错误地总是输入它们; 即使我在编写代码时我认为类型很明显,但是在我没有看了一会儿之后阅读它时它可能并不那么明显.即使是一两个必须在我头脑中进行类型推断的实例,我的手指也会有点费力!

Ingo的答案提供了一个很好的理由,故意不给出一个本地定义的类型,但我怀疑主要原因是许多程序员已经同化了经验法则,即为顶级定义提供类型声明,但不提供本地定义的方式他们学会了哈斯克尔.


Don*_*art 11

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

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

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