代数数据类型 #
一、代数数据类型基础 #
1.1 什么是代数数据类型 #
代数数据类型(ADT)是Haskell中定义自定义类型的方式:
haskell
-- 使用data关键字定义
data Bool = True | False
-- Bool是类型名
-- True和False是数据构造器
1.2 基本语法 #
haskell
-- 语法
data 类型名 = 构造器1 | 构造器2 | ...
-- 示例
data Direction = North | East | South | West
-- 使用
move :: Direction -> String
move North = "向北"
move East = "向东"
move South = "向南"
move West = "向西"
二、和类型 #
2.1 枚举类型 #
和类型表示"或"关系,多个构造器选择其一:
haskell
-- 枚举类型
data Day = Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday
-- 模式匹配
isWeekend :: Day -> Bool
isWeekend Saturday = True
isWeekend Sunday = True
isWeekend _ = False
nextDay :: Day -> Day
nextDay Monday = Tuesday
nextDay Tuesday = Wednesday
nextDay Wednesday = Thursday
nextDay Thursday = Friday
nextDay Friday = Saturday
nextDay Saturday = Sunday
nextDay Sunday = Monday
2.2 带数据的构造器 #
haskell
-- 构造器可以携带数据
data Shape
= Circle Double -- 半径
| Rectangle Double Double -- 宽 高
| Triangle Double Double Double -- 三边
-- 计算面积
area :: Shape -> Double
area (Circle r) = pi * r * r
area (Rectangle w h) = w * h
area (Triangle a b c) =
let s = (a + b + c) / 2
in sqrt (s * (s - a) * (s - b) * (s - c))
2.3 Maybe类型 #
haskell
-- Maybe类型定义
data Maybe a = Nothing | Just a
-- 使用
safeDiv :: Int -> Int -> Maybe Int
safeDiv _ 0 = Nothing
safeDiv x y = Just (x `div` y)
-- 处理Maybe
fromMaybe :: a -> Maybe a -> a
fromMaybe def Nothing = def
fromMaybe _ (Just x) = x
2.4 Either类型 #
haskell
-- Either类型定义
data Either a b = Left a | Right b
-- 使用
safeDivEither :: Int -> Int -> Either String Int
safeDivEither _ 0 = Left "Division by zero"
safeDivEither x y = Right (x `div` y)
-- 处理Either
either :: (a -> c) -> (b -> c) -> Either a b -> c
either f _ (Left x) = f x
either _ g (Right y) = g y
三、积类型 #
3.1 基本积类型 #
积类型表示"和"关系,包含多个字段:
haskell
-- 积类型
data Point = Point Double Double
-- 使用
distance :: Point -> Point -> Double
distance (Point x1 y1) (Point x2 y2) =
sqrt ((x2 - x1)^2 + (y2 - y1)^2)
-- 创建
origin = Point 0 0
p1 = Point 3 4
3.2 带类型参数 #
haskell
-- 带类型参数的积类型
data Pair a b = Pair a b
-- 使用
pairSum :: Pair Int Int -> Int
pairSum (Pair x y) = x + y
-- 多态函数
swapPair :: Pair a b -> Pair b a
swapPair (Pair x y) = Pair y x
3.3 多字段构造器 #
haskell
-- 多字段
data Person = Person String Int String
-- 姓名 年龄 邮箱
-- 使用
getName :: Person -> String
getName (Person name _ _) = name
getAge :: Person -> Int
getAge (Person _ age _) = age
getEmail :: Person -> String
getEmail (Person _ _ email) = email
四、记录语法 #
4.1 基本记录 #
haskell
-- 记录语法
data Person = Person
{ personName :: String
, personAge :: Int
, personEmail :: String
}
-- 创建
john :: Person
john = Person
{ personName = "John"
, personAge = 30
, personEmail = "john@example.com"
}
-- 自动生成访问函数
-- personName :: Person -> String
-- personAge :: Person -> Int
-- personEmail :: Person -> String
4.2 记录更新 #
haskell
-- 更新记录
olderJohn :: Person
olderJohn = john { personAge = 31 }
-- 更新多个字段
updatedJohn :: Person
updatedJohn = john
{ personAge = 31
, personEmail = "john.doe@example.com"
}
4.3 记录模式匹配 #
haskell
-- 模式匹配记录
showPerson :: Person -> String
showPerson Person { personName = name, personAge = age } =
name ++ " is " ++ show age ++ " years old"
-- 简写形式(变量名与字段名相同)
showPerson' :: Person -> String
showPerson' Person { personName, personAge } =
personName ++ " is " ++ show personAge ++ " years old"
五、递归数据类型 #
5.1 列表类型 #
haskell
-- 自定义列表
data List a = Nil | Cons a (List a)
-- 使用
listToList :: List a -> [a]
listToList Nil = []
listToList (Cons x xs) = x : listToList xs
listToList' :: [a] -> List a
listToList' [] = Nil
listToList' (x:xs) = Cons x (listToList' xs)
5.2 二叉树 #
haskell
-- 二叉树
data Tree a = Leaf | Node a (Tree a) (Tree a)
-- 创建树
sampleTree :: Tree Int
sampleTree = Node 5
(Node 3 (Node 1 Leaf Leaf) (Node 4 Leaf Leaf))
(Node 7 (Node 6 Leaf Leaf) (Node 8 Leaf Leaf))
-- 遍历
inorder :: Tree a -> [a]
inorder Leaf = []
inorder (Node x left right) = inorder left ++ [x] ++ inorder right
-- 深度
depth :: Tree a -> Int
depth Leaf = 0
depth (Node _ left right) = 1 + max (depth left) (depth right)
5.3 表达式类型 #
haskell
-- 数学表达式
data Expr
= Lit Int
| Add Expr Expr
| Mul Expr Expr
| Sub Expr Expr
| Div Expr Expr
-- 求值
eval :: Expr -> Maybe Int
eval (Lit n) = Just n
eval (Add e1 e2) = (+) <$> eval e1 <*> eval e2
eval (Mul e1 e2) = (*) <$> eval e1 <*> eval e2
eval (Sub e1 e2) = (-) <$> eval e1 <*> eval e2
eval (Div e1 e2) =
case eval e2 of
Just 0 -> Nothing
Just n2 -> (`div` n2) <$> eval e1
Nothing -> Nothing
六、类型参数 #
6.1 单类型参数 #
haskell
-- Maybe类型
data Maybe a = Nothing | Just a
-- 使用不同类型
maybeInt :: Maybe Int
maybeInt = Just 5
maybeString :: Maybe String
maybeString = Just "hello"
maybeNothing :: Maybe a
maybeNothing = Nothing
6.2 多类型参数 #
haskell
-- Either类型
data Either a b = Left a | Right b
-- 使用
eitherIntString :: Either Int String
eitherIntString = Left 42
eitherStringInt :: Either String Int
eitherStringInt = Right 100
6.3 幻影类型参数 #
haskell
-- 幻影类型(类型参数不在构造器中使用)
data Tagged tag a = Tagged a
-- 使用类型标记
data USD
data EUR
type Money currency = Tagged currency Double
usd :: Money USD
usd = Tagged 100.0
eur :: Money EUR
eur = Tagged 85.0
-- 编译时区分不同货币
-- addMoney :: Money a -> Money a -> Money a
七、严格字段 #
7.1 严格标记 #
haskell
-- 使用!标记严格字段
data StrictPair = StrictPair !Int !Int
-- 惰性版本
data LazyPair = LazyPair Int Int
-- 严格字段在构造时立即求值
7.2 严格数据类型 #
haskell
{-# LANGUAGE BangPatterns #-}
-- 所有字段严格
data StrictData = StrictData
{ field1 :: !Int
, field2 :: !String
}
-- 或使用严格模式
strictFunction :: StrictData -> Int
strictFunction (StrictData !x !y) = x + length y
八、派生实例 #
8.1 deriving子句 #
haskell
-- 自动派生类型类实例
data Color = Red | Green | Blue
deriving (Show, Eq, Ord, Enum, Bounded)
-- Show
show Red -- "Red"
-- Eq
Red == Green -- False
-- Ord
Red < Green -- True
-- Enum
succ Red -- Green
-- Bounded
minBound :: Color -- Red
maxBound :: Color -- Blue
8.2 派生Functor #
haskell
{-# LANGUAGE DeriveFunctor #-}
-- 自动派生Functor
data Tree a = Leaf a | Node (Tree a) (Tree a)
deriving (Show, Functor)
-- 使用
tree = Node (Leaf 1) (Leaf 2)
fmap (*2) tree -- Node (Leaf 2) (Leaf 4)
8.3 派生多个类型类 #
haskell
data Person = Person
{ name :: String
, age :: Int
} deriving (Show, Eq, Ord)
-- Show
show (Person "John" 30) -- "Person {name = \"John\", age = 30}"
-- Eq
Person "John" 30 == Person "John" 30 -- True
-- Ord(按字段顺序比较)
Person "Alice" 25 < Person "Bob" 30 -- True
九、实践示例 #
9.1 状态机 #
haskell
-- 门状态
data DoorState = Opened | Closed | Locked
deriving (Show, Eq)
-- 门
data Door = Door
{ doorState :: DoorState
, doorCode :: Maybe String
}
-- 操作
openDoor :: Door -> Maybe Door
openDoor door@Door { doorState = Closed } =
Just door { doorState = Opened }
openDoor _ = Nothing
closeDoor :: Door -> Door
closeDoor door = door { doorState = Closed }
lockDoor :: String -> Door -> Maybe Door
lockDoor code door@Door { doorState = Closed } =
Just door { doorState = Locked, doorCode = Just code }
lockDoor _ _ = Nothing
9.2 计算器 #
haskell
-- 运算符
data Op = Add | Sub | Mul | Div
deriving (Show, Eq)
-- 表达式
data Calc
= Number Double
| Binary Op Calc Calc
deriving (Show)
-- 求值
calc :: Calc -> Maybe Double
calc (Number n) = Just n
calc (Binary Add c1 c2) = (+) <$> calc c1 <*> calc c2
calc (Binary Sub c1 c2) = (-) <$> calc c1 <*> calc c2
calc (Binary Mul c1 c2) = (*) <$> calc c1 <*> calc c2
calc (Binary Div c1 c2) =
case calc c2 of
Just 0 -> Nothing
Just n -> (/ n) <$> calc c1
Nothing -> Nothing
9.3 JSON表示 #
haskell
-- 简化的JSON类型
data Json
= JsonNull
| JsonBool Bool
| JsonNumber Double
| JsonString String
| JsonArray [Json]
| JsonObject [(String, Json)]
deriving (Show, Eq)
-- 示例
sampleJson :: Json
sampleJson = JsonObject
[ ("name", JsonString "John")
, ("age", JsonNumber 30)
, ("active", JsonBool True)
, ("tags", JsonArray [JsonString "haskell", JsonString "functional"])
]
十、总结 #
代数数据类型要点:
- data关键字:定义自定义类型
- 和类型:多个构造器,表示"或"关系
- 积类型:单构造器多字段,表示"和"关系
- 记录语法:命名字段,自动生成访问函数
- 递归类型:类型可以引用自身
- 类型参数:参数化类型实现多态
- 派生实例:自动生成类型类实例
掌握代数数据类型后,让我们继续学习记录语法。
最后更新:2026-03-27