繁体   English   中英

何时在Haskell中利用类型推断?

[英]When to exploit type inference in Haskell?

我很好奇经验丰富的Haskell程序员在实践中经常使用类型推理。 我经常认为它比某些其他语言所需的永远明确的声明更有优势,但由于某种原因(也许只是因为我是新的),它“感觉”在一直写一个类型签名是正确的。我确信在某些情况下确实是必需的。

一些有经验的Haskellers(Haskellites?Haskellizers?)能提供一些输入吗?

即使您编写类型签名,它仍然是一个优势,因为编译器将捕获函数中的类型错误。 我通常也会写类型签名,但是在wherelet子句中省略它们,你实际定义新符号但不需要指定类型签名。

愚蠢的例子用奇怪的方法来计算数字的平方:

squares :: [Int]
squares = sums 0 odds
  where
    odds = filter odd [1..]
    sums s (a:as) = s : sums (s+a) as

square :: Int -> Int
square n = squares !! n

如果编译器不会自动推断它们,则oddssums是需要类型签名的函数。

此外,如果您像通常那样使用泛型函数,则类型推断可以确保您以有效的方式将所有这些泛型函数组合在一起。 如果你在上面的例子中说

squares :: [a]
squares = ...

编译器可以推断出这种方式无效,因为其中一个使用过的函数(标准库中的odd函数)需要a类型为Integral类。 在其他语言中,您通常只会在以后识别它。

如果在C ++中将其写为模板,则在非Integral类型上使用该函数时会出现编译器错误,但在定义模板时则不会。 这可能会让人感到困惑,因为它不会立即清楚您出错的地方,您可能需要查看一长串错误消息才能找到问题的真正根源。 在像python这样的东西你会在某个意想不到的点上在运行时得到错误,因为某些东西没有预期的成员函数。 在更松散的类型语言中,您可能不会收到任何错误,但只会出现意外结果。

在Haskell中,编译器可以确保可以使用其签名中指定的所有类型调用该函数,即使它是对于满足某些约束(也称为类型类)的所有类型都有效的泛型函数。 这使得以通用方式编程并使用通用库变得容易,这在其他语言中更难以正确使用。 即使您指定了泛型类型签名,编译器中仍会进行大量类型推断,以找出每次调用中使用的特定类型以及此类型是否满足函数的所有要求。

我总是为顶级函数和值编写类型签名,但不是“where”,“let”或“do”子句中的东西。

首先,通常导出顶级函数,Haddock需要一个类型声明来生成文档。

其次,当你犯了一个错误时,如果编译器有可用的类型信息,编译器错误就更容易解码。 事实上,有时在一个复杂的“where”子句中,我得到一个难以理解的类型错误,所以我添加临时类型声明来查找问题,有点像printf调试的类型级别的等价物。

因此,为了回答原始问题,我使用类型推断很多但不是100%的时间。

你有很好的直觉。 因为它们由编译器检查,所以为顶级值键入签名可提供宝贵的文档。

和其他人一样,我几乎总是为顶级函数设置一个类型签名,而且几乎从不用于任何其他声明。

另一个地方类型推断是非常有价值的是在交互循环中(例如,使用GHCi)。 当我设计和调试一些花哨的新的高阶函数或类似函数时,这种技术最有用。

当您遇到类型检查错误时,尽管Haskell编译器确实提供了有关错误的信息,但此信息可能难以解码。 为了简化操作,您可以注释掉函数的类型签名,然后查看编译器推断出的类型,并了解它与预期类型的​​不同之处。

另一个用途是当你在顶级函数中构造一个“内部函数”但你不确定如何构建内部函数甚至它的类型应该是什么。 你可以做的是将内部函数作为参数传递给顶级函数,然后向ghci询问类型级函数的类型。 这将包括内部函数的类型。 然后,您可以使用Hoogle等工具查看此函数是否已存在于库中。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM