简体   繁体   English

Haskell中函数类型的区别

[英]Difference in Function typing in Haskell

I've been playing around with basic functions in Haskell, and am a little confused with the difference between the following type declarations for the function f 我一直在玩Haskell中的基本函数,并且对函数f的以下类型声明之间的区别有点困惑

f :: Integer -> Integer

versus

f :: Integral n => n -> n

So far, I've treated both of these as identical, but I'm sure this isn't true. 到目前为止,我已将这两者视为相同,但我确信这不是真的。 What is the difference? 有什么区别?

Edit: As a response to the first answer, I wanna propose a similar example which more is along the lines of the question I hold. 编辑:作为对第一个答案的回答,我想提出一个类似的例子,更多的是我所持的问题。

Consider the following declarations 请考虑以下声明

f :: Num n => n -> n

or 要么

f :: Num -> Num

What functionality does each offer? 每个提供什么功能?

Let's rename: 我们重命名:

f :: Integer -> Integer
g :: (Integral n) => n -> n

I like to follow a fairly common practice of adding parentheses to the constraint section of the signature. 我喜欢遵循一种相当常见的做法,即在签名的约束部分添加括号。 It helps it stand out as different. 它有助于它脱颖而出。

f :: Integer -> Integer is simple, it takes an integer and returns another integer. f :: Integer -> Integer很简单,它取一个整数并返回另一个整数。

As for g :: (Integral n) => n -> n : Integral is not a type itself, rather it's more like a predicate. 至于g :: (Integral n) => n -> nIntegral本身不是一个类型,而是更像谓词。 Some types are Integral , others aren't. 有些类型是Integral ,有些类型不是。 For example, Int is an Integral type, Double is not. 例如, IntIntegral类型, Double不是。

Here n is a type variable , and it can refer to any type. 这里n是一个类型变量 ,它可以引用任何类型。 (Integral n) is a constraint on the type variable, which restricts what types it can refer to. (Integral n)是对类型变量的约束 ,它限制了它可以引用的类型。 So you could read it like this: 所以你可以这样读:

g takes a value of any type n and returns a value of that same type , provided that it is an Integral type. g任何类型 n的值并返回相同类型的值,前提是它是一个Integral类型。

If we examine the Integral typeclass: 如果我们检查Integral类型类:

ghci> :info Integral
class (Real a, Enum a) => Integral a where
  quot :: a -> a -> a
  rem :: a -> a -> a
  div :: a -> a -> a
  mod :: a -> a -> a
  quotRem :: a -> a -> (a, a)
  divMod :: a -> a -> (a, a)
  toInteger :: a -> Integer
 {-# MINIMAL quotRem, toInteger #-}
    -- Defined in ‘GHC.Real’
instance Integral Word -- Defined in ‘GHC.Real’
instance Integral Integer -- Defined in ‘GHC.Real’
instance Integral Int -- Defined in ‘GHC.Real’

We can see 3 builtin types which are Integral . 我们可以看到3种内置类型,即Integral Which means that g simultaneously has three different types, depending on how it is used. 这意味着g 同时具有三种不同的类型,具体取决于它的使用方式。

g :: Word -> Word
g :: Integer -> Integer
g :: Int -> Int

(And if you define another Integral type in the future, g will automatically work with that as well) (如果你将来定义另一个Integral类型, g也会自动使用它)

The Word -> Word variant is a good example, since Word s cannot be negative. Word -> Word变体是一个很好的例子,因为Word不能是否定的。 g , when given a positive machine-sized number, returns another positive machine-sized number, whereas f could return any integer, including negative ones or gigantic ones. g ,当给出一个正的机器大小的数字时,返回另一个正的机器大小的数字,而f可以返回任何整数,包括负整数或巨大的整数。

Integral is a rather specific class. Integral是一个相当具体的类。 It's easier to see with Num , which has fewer methods and thus can represent more types: 使用Num更容易看到, Num具有更少的方法,因此可以代表更多类型:

h :: (Num a) => a -> a

This is also a generalization of f , that is, you could use h where something with f 's type is expected. 这也是f的推广,也就是说,你可以使用h来预期带有f类型的东西。 But h can also take a complex number, and it would then return a complex number. 但是h也可以采用复数,然后返回一个复数。

The key with signatures like g 's and h 's is that they work on multiple types, as long as the return type is the same as the input type. 具有gh等签名的密钥是它们可以处理多种类型,只要返回类型与输入类型相同即可。

According to this link the standard instances of the Integral type class are Integer and Int . 根据此链接Integral类型类的标准实例是IntegerInt Integer is a primitive type, and it acts like an unbounded mathematical integer. Integer是一种原始类型,它的作用类似于无界数学整数。 So given: 所以给出:

f :: Integral n => n -> n

n could be Int or Integer or any "custom" type that you define that is an instance of Integral . n可以是IntInteger ,也可以是您定义的任何“自定义”类型,它是Integralinstance The use of type classes allows for type polymorphism. 类类的使用允许类型多态。

f :: Num -> Num WILL NOT COMPILE because Num is not a type. f :: Num -> Num 不会编译,因为Num 不是一个类型。 Num has kind * -> Constraint and is thus a type constructor whereas f requires an ordinary type or monotype which has kind * , such as Int or Integer (also known as type system primitives). Num具有类型* -> Constraint ,因此是类型构造函数,f需要具有类型*的普通类型单一类型,例如IntInteger (也称为类型系统基元)。 See the haskell wiki for a brief reference/jumping off point about kinds . 请参阅haskell wiki以获取有关种类的简要参考/跳跃点。 Haskell has a rich type system with higher-order types, if used correctly it can be an extremely powerful tool ie type polymorphism. Haskell有一个具有高阶类型的丰富类型系统,如果正确使用它可以是一个非常强大的工具,即类型多态。

f :: Integer -> Integer

f is a function from arbitrary sized integers to arbitrary sized integers. f是从任意大小的整数到任意大小的整数的函数。

f' :: Integral n => n -> n

f' is a polymorphic function from type n to type n . f'是从n型到n型的多态函数。 Any type n that is member of the class Integral is allowed. 允许任何类型为Integral类成员的类型n Examples for types in Integral are Integer and Int (the integer type with fixed precision). Integral中类型的示例是IntegerInt (具有固定精度的整数类型)。

f'' :: Num n => n -> n

f'' is also a polymorphic function, but this time for class Num . f''也是一个多态函数,但这次是为了类Num Num is a more generic number type that has more members than Integral , but all types in Integral are also in contained in Num . Num是一种更通用的数字类型,其成员数量多于Integral ,但Integral中的所有类型也包含在Num For f'' n can also be Double . 对于f'' n也可以是Double

TL;DR TL; DR

f :: Integer -> Integer works for one type: Integer . f :: Integer -> Integer适用于一种类型: Integer

f :: Integral n => n -> n works for Integer , Int , Word , Int32 , Int64 , Word8 , and any arbitrary user-defined types that implement the Integral type class. f :: Integral n => n -> n适用于IntegerIntWordInt32Int64Word8以及任何实现Integral类型类的任意用户定义类型。

That's the short version. 这是短版本。 If you only care about one type, it's easier to type-check and more efficient to execute if you specify that one type. 如果您只关心一种类型,那么如果指定一种类型,则更容易进行类型检查并且执行效率更高。 If you want multiple types, then the second way is probably the way to do. 如果你想要多种类型,那么第二种方式可能就是这样。

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

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