• Eq 类型类可以使用 ==/=
  • Ord 类型类可以比较大小
  • Show 类型类可以直接转换为字符串
  • Read 类型类可以从字符串转为该类型
  • Bounded 类型类有上下界
  • Enum 类型类可以枚举,对所有值构造器都是空元的数据类型,我们可以让它成为 Enum 类型类的成员
data Day = Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday
           deriving (Eq, Ord, Show, Read, Bounded, Enum)

Monday `compare` Tuesday  --  LT
Monday < Tuesday          --  True
show Wednesday            --  "Wednesday"
read "Wednesday" :: Day   --  Wednesday
Saturday == Thursday      --  False
Sunday /= Saturday        --  True
minBound :: Day           --  Monday
maxBound :: Day           --  Sunday
succ Monday               --  Tuesday
pred Saturday             --  Friday
[Thursday .. Sunday]      --  [Thursday,Friday,Saturday,Sunday]
  • 类型别名可以使代码更易理解
  • 类型别名也可以有参数
import Data.Map

type AssocList k v = [(k,v)]

type IntMap v = Map Int v
type IntMap' = Map Int

[(1,2),(4,5),(7,9)] :: AssocList Int Int -- [(1,2),(4,5),(7,9)]
  • Either a b 数据类型
-- Defined in 'Data.Either':
data Either a b = Left a | Right b deriving (Eq, Ord, Read, Show)

:t Right 'a'  -- Right 'a' :: Either a Char
:t Left True  -- Left True :: Either Bool b


-- An example:
import qualified Data.Map as Map
data LockerState = Taken | Free deriving (Show, Eq)
type ErrorMessage = String
type Code = String
type LockerMap = Map.Map Int (LockerState, Code)

lockerLookup :: Int -> LockerMap -> Either ErrorMessage Code
lockerLookup lockerNumber map = case Map.lookup lockerNumber map of
    Nothing -> Left $ "Locker" ++ show lockerNumber ++ " doesn't exist!"
    Just (state, code) -> if state /= Taken
                          then Right code
                          else Left $ "Locker " ++ show lockerNumber ++ " is already taken!"

lockers :: LockerMap
lockers = Map.fromList
    [(100,(Taken,"ZD39I"))
    ,(101,(Free ,"JAH3I"))
    ,(105,(Free ,"QOTSA"))
    ]

lockerLookup 100 lockers  -- Left "Locker 100 is already taken!"
lockerLookup 101 lockers  -- Right "JAH3I"
lockerLookup 110 lockers  -- Left "Locker110 doesn't exist!"
  • 类型本身可以作为该类型的字段,即可以创建递归数据结构
  • [3,4,5,6]3:(4:(5:(6:[]))) 的语法糖
  • 只要以特殊字符命名函数,即可自动将其视为中缀函数
  • 值构造器本质上也是函数,但是中缀值构造器必须以冒号开头
  • infixl, infixr 固定性声明,可以指定运算符的优先级和结合性
  • 下面是 List 的两个实现
-- Version 1:
data List a = Empty | Cons a (List a) deriving (Show, Read, Eq, Ord)

3 `Cons` (4 `Cons` (5 `Cons` Empty)) -- Cons 3 (Cons 4 (Cons 5 Empty))


-- Version 2:
infixr 5 :-:
infixr 5 +++

data List a = Empty | a :-: (List a) deriving (Show, Read, Eq, Ord)

(+++) :: List a -> List a -> List a
Empty      +++ ys = ys
(x :-: xs) +++ ys = x :-: (xs +++ ys)

let a = 3 :-: 4 :-: 5 :-: Empty
let b = 6 :-: 7 :-: Empty
a          --   3 :-: (4 :-: (5 :-: Empty))
2 :-: a    --   2 :-: (3 :-: (4 :-: (5 :-: Empty)))
a +++ b    --   3 :-: (4 :-: (5 :-: (6 :-: (7 :-: Empty))))
  • 二叉树的定义
  • 二叉搜索树的插入
  • 二叉搜索树的元素查询
data Tree a = EmptyTree | Node a (Tree a) (Tree a) deriving (Show)

treeInsert :: (Ord a) => a -> Tree a -> Tree a
treeInsert x EmptyTree = Node x EmptyTree EmptyTree
treeInsert x (Node a lchild rchild)
    | x == a           = Node a lchild rchild
    | x <  a           = Node a (treeInsert x lchild) rchild
    | x >  a           = Node a lchild (treeInsert x rchild)

treeElem :: (Ord a) => a -> Tree a -> Bool
x `treeElem` EmptyTree = False
x `treeElem` (Node a lchild rchild)
    | x == a           = True
    | x <  a           = x `treeElem` lchild
    | x >  a           = x `treeElem` rchild


let nums = [8,6,4,1,7,3,5]
let numsTree = foldr treeInsert EmptyTree nums
numsTree -- Prettified output:
         -- Node 5
         --     (Node 3 
         --         (Node 1 EmptyTree EmptyTree)
         --         (Node 4 EmptyTree EmptyTree)
         --     )
         --     (Node 7
         --         (Node 6 EmptyTree EmptyTree)
         --         (Node 8 EmptyTree EmptyTree)
         --     )
8 `treeElem` numsTree -- True
9 `treeElem` numsTree -- False
  • 类型类 的概念类似其他语言中的 接口
  • 一个类型类定义了某些行为(如判断相等性、比较顺序、枚举)
  • 类型类的行为通过函数的定义或者待实现的函数声明来实现
  • 某个类型是一个类型类的实例,就拥有这个类型类约定的行为
  • Eq 类型类的定义
  • 类型变量一定要小写
  • 最小完备定义
  • 将某个类型作为某个类型类的实例
  • 子类化
-- Definition of `Eq`:
class Eq a where
    (==) :: a -> a -> Bool
    (/=) :: a -> a -> Bool
    x == y = not (x /= y)
    x /= y = not (x == y)


-- Example of instance:
data TrafficLight = Red | Yellow | Green

instance Eq TrafficLight where
    Yellow == Yellow = True
    Green  == Green  = True
    Red    == Red    = True
    _      == _      = False

instance Show TrafficLight where
    show Yellow = "Yellow Light"
    show Green  = "Green Light"
    show Red    = "Red Light"

Red == Red                -- True
Red == Yellow             -- False
Red /= Green              -- True
Red `elem` [Red, Yellow]  -- True
[Red, Yellow, Green]      -- [Red Light,Yellow Light,Green Light]


-- Example of sub-class:
class (Eq a) => Num a where
    ...
  • 作为类型类实例的带参数类型
  • GHCi 中可以用 :info 查询实例
  • 一个类似其他语言中的隐式类型转换为 boolYesNo 类型
  • [] 是一个带参数的类型,而 [a] 则是具体的类型
  • id 表示将参数原样返回
instance (Eq m) => Eq (Maybe m) where
    Just x  == Just y  = x == y
    Nothing == Nothing = True
    _       == _       = False


:info Maybe
data Maybe a = Nothing | Just a     -- Defined in ‘Data.Maybe’
instance Eq a => Eq (Maybe a) -- Defined in ‘Data.Maybe’
instance Monad Maybe -- Defined in ‘Data.Maybe’
instance Functor Maybe -- Defined in ‘Data.Maybe’
instance Ord a => Ord (Maybe a) -- Defined in ‘Data.Maybe’
instance Read a => Read (Maybe a) -- Defined in ‘GHC.Read’
instance Show a => Show (Maybe a) -- Defined in ‘GHC.Show’


-- YesNo:
data Tree a = EmptyTree | Node a (Tree a) (Tree a) deriving (Show)
data TrafficLight = Red | Yellow | Green

class YesNo a where
    yesno :: a -> Bool

instance YesNo Int where
    yesno 0 = False
    yesno _ = True

instance YesNo Bool where
    yesno = id

instance YesNo [a] where
    yesno [] = False
    yesno _  = True

instance YesNo (Maybe a) where
    yesno Nothing  = False
    yesno (Just _) = True

instance YesNo (Tree a) where
    yesno EmptyTree = False
    yesno _         = True

instance YesNo TrafficLight where
    yesno Red = False
    yesno _   = True

yesno (0 :: Int)   -- False
yesno $ length []  -- False
yesno "hello"      -- True
yesno ""           -- False
yesno $ Just 0     -- True
yesno Nothing      -- False
yesno True         -- True
yesno EmptyTree    -- False
yesno [0]          -- True
yesno []           -- False
yesno Red          -- False
:t yesno           -- yesno :: YesNo a => a -> Bool


yesnoIf :: (YesNo y) => y -> a -> a -> a
yesnoIf cond yes no = if yesno cond then yes else no

yesnoIf [] "Yeah!" "No!"        -- "No!"
yesnoIf [0,1] "Yeah!" "No!"     -- "Yeah!"
yesnoIf True "Yeah!" "No!"      -- "Yeah!"
yesnoIf (Just 1) "Yeah!" "No!"  -- "Yeah!"
  • Functor 类型类用来表示可以映射的事物
  • Functor 类型类以一个接受类型参数的类型构造器作为类型参数
  • map 是仅仅处理列表的 fmap
  • Maybe 函子
  • Tree 函子
  • Either a 函子:因为 Functor 类型类期望的是取单个参数的类型构造器,而 Either 有两个参数,所以可以只交给 Either 一个参数对它进行部分应用,这样就它就只剩下一个参数了
-- Definition of `Functor`:
class Functor f where
    fmap :: (a -> b) -> f a -> f b

-- `map`:
instance Functor [] where
    fmap = map

fmap (*2) [1..3]  -- [2,4,6]
map (*2) [1..3]   -- [2,4,6]

-- `Maybe` functor, Defined in `Data.Maybe`:
instance Functor Maybe where
    fmap f (Just x) = Just (f x)
    fmap f Nothing  = Nothing

fmap (++ " world") (Just "hello") -- Just "hello world"
fmap (++ " world") Nothing        -- Nothing
fmap (*2) (Just 200)              -- Just 400
fmap (*2) Nothing                 -- Nothing


-- `Tree` functor:
data Tree a = EmptyTree | Node a (Tree a) (Tree a) deriving (Show)

treeInsert :: (Ord a) => a -> Tree a -> Tree a
treeInsert x EmptyTree = Node x EmptyTree EmptyTree
treeInsert x (Node a lchild rchild)
    | x == a           = Node a lchild rchild
    | x <  a           = Node a (treeInsert x lchild) rchild
    | x >  a           = Node a lchild (treeInsert x rchild)

instance Functor Tree where
    fmap f EmptyTree = EmptyTree
    fmap f (Node x lchild rchild) = Node (f x) (fmap f lchild) (fmap f rchild)

fmap (*2) EmptyTree -- EmptyTree
fmap (*2) (foldr treeInsert EmptyTree [2,3,1])
                    -- Node 2 EmptyTree (Node 6 (Node 4 EmptyTree EmptyTree) EmptyTree)


-- `Either a` functor, Defined in `Data.Either`
instance Functor (Either a) where
    fmap f (Right x) = Right (f x)
    fmap f (Left x)  = Left x

fmap show (Left 10)  -- Left 10
fmap show (Right 1)  -- Right "1"
  • 每个类型都有 kind
  • * 表示一个具体的类型
  • * -> * 意味着类型构造器取一个具体类型,返回一个具体类型
  • 类型构造器也能够柯里化
:k Int               -- Int :: *
:k Either            -- Either :: * -> * -> *
:k Either String     -- Either String :: * -> *
:k Either String Int -- Either String Int :: *