类型类的作用
- 若无类型类,必须为不同类型(type)实现功能类似的非重名的函数(function)以使用
- 若用类型类,则可以提高效率。例如, 如果能用 == 对比任何类型的值,就会很方便
通过类型类可以定义通用(generic)函数,该函数能对几乎任何东西(anything)合法。对于具有比较功能的函数而言,通过写一个通用函数,能够比较所有的东西,即 generic programming :如果一段代码可以进行比较,generic code应该就能够接受任何数据类型,并且对于这些类型,编译器是知道如何比较的,如果以后新类型被添加进来,现有的代码也不应该被修改。
类型类(Typeclass) 与 实例(Instance)
类型类定义一系列函数,这些函数对于不同类型而言具有不同的函数实现。
class BasicEq a where
isEqual :: a -> a -> Bool
isEqual x y = not (isNotEqual x y)
isNotEqual :: a -> a -> Bool
isNotEqual x y = not (isEqual x y)
--isEqual :: (BasicEq a) => a -> a -> Bool
--对于所有的类型 a ,只要 a 是 BasicEq 的一个实例,isEqual 接受两个类型为 a 的参数,返回一个 Bool
instance BasicEq Bool where--定义类型类实例
isEqual False False = True
isEqual True True = True
isEqual _ _ = False
{-类型类Eq
class Eq a where
(==), (/=) :: a -> a -> Bool
-- Minimal complete definition:
-- (==) or (/=)
x /= y = not (x == y)
x == y = not (x /= y)
-}
- 使用 class 关键字声明 typeclass ,命名为 BasicEq
- 引用命名为 a 的 instance type ,只要实现了类型类中定义的函数,实例类型可以是任何类型
- 在类型类的定义的第一行,引用实例类型的命名可以是任意的,需要注意的是若使用 a 来表示实例类型,那么函数签名中也必须使用 a 来代表这个实例类型
- 为了避免让类型类的用户为所有类型都定义两个函数,可以提供两个函数的默认实现,不过每个函数取决于另一个来计算答案,因此至少一个函数要被实现,否则代码将产生死循环
重要的内置类型类
- Show类型类用于将值转换为字符串,
show :: Show a => a -> String
show 1=="1"
show "str"=="\"str\""
putStrLn "1"->1
putStrLn "\"str\""->"str"
print 1->1
print "1"->"1"
- Read类型类用于将字符串转换为值,
read :: Read a => String -> a
为使read函数返回正确类型的值,必须给它指定正确的类型,例如read "3"
会因未指定类型导致异常,read "5.0"::Integer
会因Integer解析器不能接受小数点导致异常,正确如read "3"::Double
,值为3.0
序列化
把内存中的数据转化成为序列的过程
Prelude> let years = [1999, 2010, 2012]
Prelude> show years
"[1999,2010,2012]"
Prelude> writeFile "years.txt" (show years) #序列化
Prelude> input <- readFile "years.txt"
Prelude> input
"[1999,2010,2012]"
Prelude> (read input)::[Int] #反序列化
[1999,2010,2012]
数值类型及其对应类型类
类型 | 解释 | Bits | Bounded | Floating | Fractional | Integral | Num | Real | RealFrac |
---|---|---|---|---|---|---|---|---|---|
Double | 双精度浮点数 | X | X | X | X | X | |||
Float | 单精度浮点数 | X | X | X | X | X | |||
Int | 固定精度带符号整数 | X | X | X | X | X | |||
Integer | 任意精度带符号整数 | X | X | X | X | ||||
Rational or any Ratio | 任意精度有理数 | X | X | X | X | ||||
Word | 固定精度无符号整数 | X | X | X | X | X |
自动派生
- Haskell可以自动将类型派生为 Read 、 Show 、 Bounded 、 Enum 、 Eq 和 Ord 的实例
- 自动派生某个类型类的一个实例时,该类型的值构造器的构造子也必须是给定类型类的实例
--由于Book不是类型类Show的实例,BookInfo不能自动派生
data Book = Book
data BookInfo = BookInfo Book
deriving (Show)
newtype关键字
newtype NewtypeInt = N Int
deriving (Eq, Ord, Show)
newtype 目的是重命名一个存在着的类型,来给它一个独特的身份,声明方式与data相似。
data与newtype的区别:
- newtype 只能有一个值构造器,并且构造器仅有一个字段,newtype的作用是限制已存在类型使用
- data 关键字创建的类型,有一个簿记保持(book-keeping)的开销在运行时,追踪(track)某个值是由哪个值构造器创造的
type与newtype的区别:
- type 关键字以另一各名字引用(refer to)某个类型,即类型别名
- newtype 关键字隐藏一个类型的本性(nature),上例中 NewtypeInt 提供 Int 的 Eq 、 Ord 和 Show 实例,但用户并不知道它被实现为一个 Int,只知道它有一个“唯一标识符”(newtype)