概述

  • haskell有类型推理机制,如果我们写一个数字,我们不用告诉编译器它是一个数字,

编译器能够推理出它是一个数字。

  • 使用:t命令可以查看一个表达式的类型。
ghci> :t 'a'
'a' :: Char
ghci> :t False
False :: Bool
ghci> :t "aaa"
"aaa" :: [Char]
ghci> :t ('a', "aaa")
('a', "aaa") :: (Char, [Char])

::读作has type of 我们看到"aaa"有类型[Char],表明是一个list 而元组(‘a’, “aaa”)有类型(char, [char]) 类型的首字母都是大写

  • 函数也有类型,看下面这个函数
remove_uppercase str = [ ch | ch <- str, ch `elem` ['a'..'z']]

查看它的类型:

ghci> :t remove_uppercase
remove_uppercase :: [Char] -> [Char]

如果带多个参数会怎么样呢?

my_sum a b c = a + b + c

这样,会得到下面的结果:

ghci> :t my_sum
my_sum :: Num a => a -> a -> a -> a

这是什么乱七八糟的呢?先不管,我们给这个函数声明类型:

my_sum :: Int -> Int -> Int -> Int
my_sum a b c = a + b + c

再来看一下:

ghci> :t my_sum
my_sum :: Int -> Int -> Int -> Int

原来多个参数的类型是这样表示的。

一些基本的数据类型

  • Int

用来表示整数,它是有界的,比如32位的环境下是-2 ^ 31 - 1至2 ^ 31

  • Integer

也是用来表示整数的,但是它是无界的,于是它可以用来表示很大的数字, 虽然很大,但是你懂的,不可能无限大。可以想到,有界的Int要高效一 些。

  • Float

表示单精度浮点数

  • Double

表示双精度浮点数

  • Bool

boolean类型,只有两个值True和False

  • Char

字符

类型变量(type variable)

操作list中有一个函数叫head,它的类型是:

Prelude> :t head
head :: [a] -> a

由于a不是以大写字母开头的,所以它不是一个类型,而事实上它是一个类型变量( type variable),表明a可以是任意类型,这个有点像C++中的泛型。存在类型变量 的函数称为多态函数(polymorphic function)。 再来看看fst函数:

Prelude> :t fst
fst :: (a, b) -> a

可以看出,类型变量使用小写字母a, b, c, d…来表示。

类型类(typeclass)

一个类型类是一个定义了一些动作的接口,如果一个类型是一个类型类的一部分,则这 个类型实现了这个类型类定义的一些动作。

Prelude> :t (==)
(==) :: Eq a => a -> a -> Bool

这里,=>右边表明==这个函数接受2个任意类型的参数,返回Bool类型,左边表明参数 a必须是Eq类的成员。左边的称为类约束(class constraint)。 再看看elem:

Prelude> :t elem
elem :: Eq a => a -> [a] -> Bool

下面是一些基本的typeclass:

  • Eq,用于比较相等,它里面的成员实现的函数有=和/
  • Ord,用于操作顺序,它的成员实现的函数有>, <, >=, <=和compare
Prelude> 5 > 2
True
Prelude> compare 5 3
GT

compare接受2个相同类型的参数,返回类型Ordering.Ordering可以为GT,LT或者EQ。 要成为Ord的成员,必须要先是Eq的成员。

  • Show,用于显示字符串,它的成员实现的函数主要是show。
Prelude> show 1.23
"1.23"
  • Read,用于读取一个字符串,它的成员实现的函数主要是read,读取一个字符串,返回

一个在Read成员中有的类型。

Prelude> read "1.2" + 2.1
3.3

它是怎么知道是哪个类型的呢?看下面的例子:

Prelude> read "2"

<interactive>:19:1:
    No instance for (Read a0) arising from a use of `read'
    The type variable `a0' is ambiguous
    Possible fix: add a type signature that fixes these type variable(s)
    Note: there are several potential instances:
      instance Read () -- Defined in `GHC.Read'
      instance (Read a, Read b) => Read (a, b) -- Defined in `GHC.Read'
      instance (Read a, Read b, Read c) => Read (a, b, c)
        -- Defined in `GHC.Read'
      ...plus 25 others
    In the expression: read "2"
    In an equation for `it': it = read "2"

它提示错误了,因为它不知道是哪个类型。看一下read函数的类型:

Prelude> :t read
read :: Read a => String -> a

它返回了a,表明可以是任意变量,而上例中两个数相加,是因为编译器猜到了类型, 如果要用read函数,则最好是给它强制转换类型,通过加上::符号:

Prelude> read "2" :: Int
2
  • Enum,它的成员是顺序的有序类型,有2个函数:succ和pred,用来求某个元素

的successor和predecesor。它里面的成员有:(), Bool, Char, Ordering, Int, Integer, Float, Double。其中()是空元组类型,它们都是可以枚举的。

  • Bounded,它的成员都有一个上界和一个下界,有2个有趣的函数,minBound和

maxBound,看下例:

Prelude> :t minBound 
minBound :: Bounded a => a
Prelude> minBound :: Int
-9223372036854775808
Prelude> :t maxBound
maxBound :: Bounded a => a
Prelude> maxBound :: Char
'\1114111'

所有的元组都是它的成员:

Prelude> minBound :: (Bool, Char, Int)
(False,'\NUL',-9223372036854775808)
  • Num,顾名思义,它是一个数字的类型类。

一个数字就是一个多态常量:

Prelude> :t 2
2 :: Num a => a
Prelude> 2 :: Int
2
Prelude> 2 :: Integer
2
Prelude> 2 :: Float
2.0
Prelude> 2 :: Double
2.0

要成为Num的成员,必须要先成为Show和Eq的成员。

  • Integral,Num包括所有的数字类型,而Integral只包含整数类型(Int和Integer)。

一个有用的函数是fromIntegral,它的类型为:

Prelude> :t fromIntegral
fromIntegral :: (Integral a, Num b) => a -> b

注意到一个奇怪的地方,它的类约束有2个,用逗号隔开。

  • Floating,只包括浮点数类型(Float, Double)。