类型
Haskell 是强类型和静态类型的,所有的数据都有明确的类型
静态类型的好处
- 编译期间就可以发现很多类型导致的错误
- 支持类型推导
表达式的类型
- 使用
:t
命令可以查看表达式的类型 - 类型必须用大写字母开头
-
::
读作 “它的类型为”
ghci> :t 'a'
'a' :: Char
ghci> :t True
True :: Bool
ghci> :t "HELLO!"
"HELLO!" :: [Char]
ghci> :t (True, 'a')
(True, 'a') :: (Bool, Char)
ghci> :t 4 == 5
4 == 5 :: Bool
函数的类型
函数也有明确的类型,包括参数的类型和返回值的类型。
比如前面那个过滤大写字母的函数,输入一个字符串,输出一个字符串,类型如下
removeNonUppercase :: [Char] -> [Char]
removeNonUppercase st = [ c | c <- st, c `elem` ['A'..'Z']]
注意:
[Char]
和String
是等价的
多个参数的函数,类型之间用 ->
分隔,最后一项是输出类型
addThree :: Int -> Int -> Int -> Int
addThree x y z = x + y + z
几种常见的基本类型
类型 | 说明 |
---|---|
Int |
32 位整数,有边界 |
Integer |
大整数,没有边界 |
Float |
单精度浮点数,精度稍差 |
Double |
双精度浮点数,精度较高 |
Bool |
布尔值 |
Char |
字符类型 |
类型变量
有的时候,函数在定义的时候参数的类型不能确定,等到使用时参数传入后才可以确定类型,这时可以用一个变量代替参数类型。
比如处理列表的 head
函数。列表可以是任意类型的列表,返回值只要和列表中元素的类型一致即可。
ghci> :t head
head :: [a] -> a
类型变量都使用单个小写字母
类型类入门
TypeClasses 叫类型类,他是对类型约束和规范,他要求类型必须满足某些规格,有点类似接口。
类型被某种 TypeClasses 约束后,和这种 TypeClasses 相关的函数就可以处理被其约束的类型,
下面是 ==
函数的类型声明,该函数的参数被 Eq
约束,说明这个参数可以被 Eq
相关的函数所处理
ghci> :t (==)
(==) :: (Eq a) => a -> a -> Bool
读作: 相等函数取两个相同类型的值作为参数,传回一个布尔值。两个参数的类型必须符合 Eq
类型类的约束
Eq
规定了判断相等性的规范,凡是能够比较相等性的类型都必须被 Eq
类约束,比如数字、字符、字符串等等
ghci> 5 == 5
True
ghci> 5 /= 5
False
ghci> 'a' == 'a'
True
ghci> "Ho Ho" == "Ho Ho"
True
ghci> 3.432 == 3.432
True
elem
函数中,因为其参数要使用 ==
,所以也被 Eq
约束
ghci> :t elem
(Eq a)=>a->[a]->Bool
基本的类型类
Eq
任何需要使用相等性判断的类型,即希望该类型能够被 ==
或 /=
等函数处理,都要受 Eq
约束
Ord
需要比较大小,能够被 <, >, <=, >=, compare
等相关函数处理的类型,都受它约束
注意:要成为
Ord
的成员,首先需要加入Eq
ghci> "Abrakadabra" < "Zebra"
True
ghci> "Abrakadabra" `compare` "Zebra"
LT
ghci> 5 >= 2
True
ghci> 5 `compare` 3
GT
Show
show
函数可以将一个数据对象转换成字符串
任何需要能转换成字符串,并能够被 show
函数处理的的类型,都受它约束
ghci> show 3
"3"
ghci> show 5.334
"5.334"
ghci> show True
"True"
Read
read
函数可以将字符串转换成一个数据对象,他是 show
函数的反函数
Read
和 Show
正好相反,任何类型想要获取从字符串转换成对象的能力,即能够被 read
函数处理,都受它约束
ghci> read "True" || False
True
ghci> read "8.2" + 3.8
12.0
ghci> read "5" - 2
3
ghci> read "[1,2,3,4]" ++ [3]
[1,2,3,4,3]
转换时须明确类型
需要注意的是,如果字符串转换的类型不能推断,则需要明确指定,否则会报错
下面代码中,4
既可以转换成 Int
类型,也可以转换成 Integer
Float
类型,因为没有明确指定,编译器会报错
ghci> read "4"
< interactive >:1:0:
Ambiguous type variable `a' in the constraint:
`Read a' arising from a use of `read' at <interactive>:1:0-7
Probable fix: add a type signature that fixes these type variable(s)
下面明确指定类型
ghci> read "5" :: Int
5
ghci> read "5" :: Float
5.0
ghci> (read "5" :: Float) * 4
20.0
ghci> read "[1,2,3,4]" :: [Int]
[1,2,3,4]
ghci> read "(3, 'a')" :: (Int, Char)
(3, 'a')
Enum
连续类型,有前置和后继,能够被 succ
和 pred
等函数处理。
相关类型有 (), Bool, Char, Ordering, Int, Integer, Float 和 Double
ghci> ['a'..'e']
"abcde"
ghci> [LT .. GT]
[LT,EQ,GT]
ghci> [3 .. 5]
[3,4,5]
ghci> succ 'B'
'C'
Bounded
有上下边界的类型,能够被 minBound
和 maxBound
等函数处理
ghci> minBound :: Int
-2147483648
ghci> maxBound :: Char
'\1114111'
ghci> maxBound :: Bool
True
ghci> minBound :: Bool
False
注意,如果元组中的分量都是有边界的,那么元组也是有边界的
ghci> maxBound :: (Bool, Int, Char)
(True,2147483647,'\1114111')
Num
如果你的类型需要进行四则运算,即能够使用 + - * /
等相关函数,则应该被 Num
约束
下面的代码说明, *
要求的参数类型虽然还不确定,但是只要符合 Num
规范就可以,因此传给他 Int Float
都可以
ghci> :t (*)
(*) :: (Num a) => a -> a -> a
注意,乘号两边的数字类型必须一致,否则会报错,比如
(5 :: Int) * (6 :: Integer)
Integral
对所有整数的规范
Floating
对所有浮点数的规范
重要函数 fromIntegral
这个函数会将一个对象的约束条件从 Integral
转换成 Num
,受 Num
约束的数是更通用的数,可以使用更多的函数
fromIntegral :: (Num b, Integral a) => a -> b
历史问题
由于历史原因,某些函数会返回整数类型,而不是通用数字类型。这时候如果用返回值和其他数字类型运算会出错。
举例来说,length
函数的型别声明为:
length :: [a] -> Int
如果用 length
的返回值,给它加一个浮点数 3.2
就会报错,因为这是将浮点数和整数相加。
这时候就需要
fromIntegral (length [1,2,3,4]) + 3.2
</br>
网友评论