简体   繁体   English

Haskell何时需要类型签名?

[英]When are type signatures necessary in Haskell?

Many introductory texts will tell you that in Haskell type signatures are "almost always" optional. 许多介绍性文本将告诉您,在Haskell类型签名中,“几乎总是”是可选的。 Can anybody quantify the "almost" part? 有人可以量化“几乎”部分吗?

As far as I can tell, the only time you need an explicit signature is to disambiguate type classes. 据我所知, 唯一需要显式签名的就是消除类型类的歧义。 (The canonical example being read . show .) Are there other cases I haven't thought of, or is this it? (正在read . show的典型示例。show。)还有其他我没有想到的情况吗?

(I'm aware that if you go beyond Haskell 2010 there are plenty for exceptions. For example, GHC will never infer rank-N types. But rank-N types are a language extension, not part of the official standard [yet].) (我知道,如果您超越Haskell 2010,会有很多例外情况。例如,GHC永远不会推断N级类型。但是N级类型是语言的扩展,不是官方标准的一部分[尚未]。 )

Polymorphic recursion needs type annotations, in general. 通常,多态递归需要类型注释。

f :: (a -> a) -> (a -> b) -> Int -> a -> b
f f1 g n x = 
    if n == (0 :: Int)
    then g x
    else f f1 (\z h -> g (h z)) (n-1) x f1

(Credit: Patrick Cousot) (来源:帕特里克·库索)

Note how the recursive call looks badly typed (!): it calls itself with five arguments, despite f having only four ! 注意递归调用看起来多么严重类型(!):它调用本身带有五个参数,尽管f只有4级 Then remember that b can be instantiated with c -> d , which causes an extra argument to appear. 然后记住,可以使用c -> d实例化b ,这会导致出现一个额外的参数。

The above contrived example computes 上面的人为例子计算

f f1 g n x = g (f1 (f1 (f1 ... (f1 x))))

where f1 is applied n times. 其中f1被应用了n次。 Of course, there is a much simpler way to write an equivalent program. 当然,有一种编写等效程序的简单得多的方法。

Monomorphism restriction 单态限制

If you have MonomorphismRestriction enabled, then sometimes you will need to add a type signature to get the most general type: 如果启用了MonomorphismRestriction ,则有时需要添加类型签名以获得最通用的类​​型:

{-# LANGUAGE MonomorphismRestriction #-}
-- myPrint :: Show a => a -> IO ()
myPrint = print
main = do
  myPrint ()
  myPrint "hello"

This will fail because myPrint is monomorphic. 这将失败,因为myPrint是单态的。 You would need to uncomment the type signature to make it work, or disable MonomorphismRestriction . 您需要取消注释类型签名以使其起作用,或禁用MonomorphismRestriction

Phantom constraints 幻影约束

When you put a polymorphic value with a constraint into a tuple, the tuple itself becomes polymorphic and has the same constraint: 当您将具有约束的多态值放入元组时,元组本身将变为多态并具有相同的约束:

myValue :: Read a => a
myValue = read "0"

myTuple :: Read a => (a, String)
myTuple = (myValue, "hello")

We know that the constraint affects the first part of the tuple but does not affect the second part. 我们知道,约束影响的元组的第一部分,但影响第二部分。 The type system doesn't know that, unfortunately, and will complain if you try to do this: 不幸的是,类型系统不知道这一点,如果您尝试这样做,将会抱怨:

myString = snd myTuple

Even though intuitively one would expect myString to be just a String , the type checker needs to specialize the type variable a and figure out whether the constraint is actually satisfied. 即使直觉上会期望myString只是一个String ,类型检查器仍需要专门化类型变量a并确定约束是否真正得到满足。 In order to make this expression work, one would need to annotate the type of either snd or myTuple : 为了使该表达式起作用,需要注释sndmyTuple的类型:

myString = snd (myTuple :: ((), String))

In Haskell, as I'm sure you know, types are inferred . 可以肯定,您可以在Haskell中推断类型。 In other words, the compiler works out what type you want. 换句话说,编译器可以计算出所需的类型。

However, in Haskell, there are also polymorphic typeclasses, with functions that act in different ways depending on the return type. 但是,在Haskell中,也存在多态类型类,根据返回类型,函数以不同的方式起作用。 Here's an example of the Monad class, though I haven't defined everything: 这是Monad类的示例,尽管我还没有定义所有内容:

class Monad m where
    return :: a -> m a
    (>>=) :: m a -> (a -> m b) -> m b
    fail :: String -> m a

We're given a lot of functions with just type signatures. 我们给了很多带有类型签名的功能。 Our job is to make instance declarations for different types that can be treated as Monads, like Maybe t or [t] . 我们的工作是为可以视为Monad的不同类型进行实例声明,例如Maybe t[t]

Have a look at this code - it won't work in the way we might expect: 看一下这段代码-它不能像我们期望的那样工作:

return 7

That's a function from the Monad class, but because there's more than one Monad, we have to specify what return value/type we want, or it automatically becomes an IO Monad. 那是Monad类的一个函数,但是因为有多个Monad,所以我们必须指定所需的返回值/类型,否则它会自动成为IO Monad。 So: 所以:

return 7 :: Maybe Int
-- Will return...
Just 7

return 6 :: [Int]
-- Will return...
[6]

This is because [t] and Maybe have both been defined in the Monad type class. 这是因为[t]Maybe都已在Monad类型类中定义。

Here's another example, this time from the random typeclass. 这是另一个示例,这一次来自随机类型类。 This code throws an error: 此代码引发错误:

random (mkStdGen 100)

Because random returns something in the Random class, we'll have to define what type we want to return, with a StdGen object tupelo with whatever value we want: 因为random返回Random类中的某些内容,所以我们必须使用想要的StdGen对象StdGen定义想要返回的类型:

random (mkStdGen 100) :: (Int, StdGen)
-- Returns...
(-3650871090684229393,693699796 2103410263)

random (mkStdGen 100) :: (Bool, StdGen)
-- Returns...
(True,4041414 40692)

This can all be found at learn you a Haskell online, though you'll have to do some long reading. 在网上学习Haskell可以找到所有这些内容,尽管您需要做一些长时间的阅读。 This, I'm pretty much 100% certain, it the only time when types are necessary. 我几乎100%地确定,这是唯一需要类型的时间。

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

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