一、基础概念铺垫:模块、类型类、标准库类型类
在学习标准库类型类前,必须先掌握「模块」的概念——它是标准库组织代码的核心方式,类型类的定义、实现均依赖模块管理。
1. 什么是 Haskell 模块(Module)?
模块是 Haskell 中「代码组织和命名空间管理」的基本单元,类比于其他语言的“包”或“命名空间”,核心作用有3点:
- 封装代码:将相关的类型、类型类、函数集中放在一起,避免命名冲突(比如不同模块可定义同名函数,但通过模块导入区分);
- 控制可见性:通过「导入(import)」指定需要使用的模块内容,无需加载整个标准库;
- 分类管理:标准库按功能将类型类、函数拆分到不同模块,方便查阅和使用(比如数值相关在
Numeric模块,容器相关在Data模块)。
模块的基本使用示例:
-- 1. 导入模块(默认导入模块所有内容)
import Data.List -- 导入列表操作相关模块(含sort、filter等函数)
-- 2. 导入模块的指定内容(推荐,避免命名冲突)
import Data.List (sort, filter)
-- 3. 隐藏模块中的指定内容
import Data.List hiding (sort)
-- 4. qualified导入(显式指定模块前缀,彻底避免冲突)
import qualified Data.List as L
L.sort [3,1,2] -- 必须用模块前缀L.调用sort函数
-- 5. 预加载模块(无需手动导入)
-- Prelude是Haskell的默认预加载模块,包含最核心的类型类和函数
-- 比如Eq、Ord、Show等类型类,无需import就能直接使用
关键说明:Haskell 标准库的核心模块集中在 base 包(GHC 默认自带,无需额外安装),后续介绍的所有类型类,均来自 base 包下的不同模块。
2. 什么是 Haskell 类型类(Type Class)?
类型类是 Haskell 中「对“行为/接口”的抽象定义」,和面向对象(OOP)中的“接口”思想类似,但本质是「基于行为的多态」(而非基于继承),核心逻辑:
定义一套“方法规范”(比如“判断两个值是否相等”“对容器内的值进行映射”),任何类型只要实现了这套规范中的所有方法,就属于这个类型类,就能复用该类型类的所有通用功能。
通俗示例:
定义一个简单的 Eq 类型类(标准库内置,此处仅作演示),规范“判断相等”的行为:
-- 类型类定义:规范方法(==)和(/=)
class Eq a where
(==) :: a -> a -> Bool -- 判断两个a类型的值是否相等
(/=) :: a -> a -> Bool -- 判断两个a类型的值是否不相等
-- 可提供默认实现,减少重复代码
x /= y = not (x == y)
-- 让Int类型实现Eq类型类(标准库已内置,无需手动写)
instance Eq Int where
0 == 0 = True
n == m = (n - m) == 0
-- 此时Int类型就属于Eq类,可使用(==)和(/=)方法
1 == 2 -- 输出False,复用Eq类的方法
3. 什么是 Haskell 标准库类型类?
标准库类型类,是 Haskell 官方(通过 base 包)提供的、覆盖所有通用场景的类型类集合,核心特点:
- 通用性强:覆盖“值比较、数值运算、容器操作、错误处理、泛型编程”等所有日常开发场景;
- 无需重复实现:标准库已为所有基础类型(
Int、String、Maybe等)实现了核心类型类; - 可扩展性强:开发者可基于标准库类型类的设计模式,自定义业务相关的类型类。
官方核心入口:Haskell base 包文档(建议选择对应 GHC 版本,如 GHC 9.8 对应 base-4.19.0.0,可查看所有标准库模块和类型类的完整定义)。
二、标准库类型类的官方查阅渠道
标准库类型类均集中在 base 包下,以下3种渠道覆盖“在线、便捷、离线”所有场景,权威且实用:
1. 官方在线文档(最全面,首选)
核心优势:内容完整、更新及时,可直接查看类型类的定义、方法、默认实现和实例,步骤如下:
- 打开核心入口:Haskell base 包文档;
- 选择对应版本:根据自己使用的 GHC 版本选择(比如 GHC 9.8 对应
base-4.19.0.0); - 导航到类型类:点击页面顶部的「Modules」,标准库核心类型类按模块分类存放,快速导航表如下(重点记忆,方便快速查找):
| 模块名 | 核心类型类 | 用途 | 是否需要手动导入 |
|---|---|---|---|
Prelude | Eq、Ord、Show、Read、Num、Functor、Monad 等 | 最核心的类型类和函数 | 否(默认预加载) |
Data.Eq | Eq | 相等性判断 | 否(Prelude 已导入) |
Data.Ord | Ord、Ordering | 大小比较、排序 | 否(Prelude 已导入) |
Data.Foldable | Foldable | 容器折叠(遍历/聚合) | 否(Prelude 已导入,部分方法需显式调用) |
Data.Traversable | Traversable | 可遍历容器(带副作用映射) | 是(需手动 import Data.Traversable) |
Control.Monad | Monad、MonadPlus、MonadIO 等 | 单子(顺序依赖计算) | 否(Prelude 已导入核心方法) |
Control.Applicative | Applicative、Alternative | 应用函子(多参数映射) | 否(Prelude 已导入) |
Data.Functor | Functor、Contravariant | 函子(单参数映射) | 否(Prelude 已导入核心方法) |
Numeric.Num | Num、Fractional、Integral 等 | 数值运算 | 否(Prelude 已导入) |
Data.Semigroup | Semigroup | 半群(结合律二元操作) | 是(需手动 import Data.Semigroup) |
Data.Monoid | Monoid | 幺半群(半群+单位元) | 是(需手动 import Data.Monoid) |
2. GHCi 交互式查询(最便捷,边写边查)
在终端打开 GHCi(安装 Haskell 后直接输入 ghci 即可启动),可实时查询类型类的定义、实例、方法,无需切换页面,适合实战开发时快速查阅,核心命令及示例如下:
-- 1. 查看类型类的完整定义(含方法、默认实现、定义模块)
-- 示例:查看Functor类型类
ghci> :info Functor
class Functor (f :: * -> *) where
fmap :: (a -> b) -> f a -> f b
(<$) :: a -> f b -> f a
{-# MINIMAL fmap #-} -- 最小实现要求:只需实现fmap即可
-- Defined in ‘GHC.Base’ -- 定义在GHC.Base模块(Prelude依赖该模块)
instance Functor [] -- Defined in ‘GHC.Base’ -- 列表是Functor的实例
instance Functor Maybe -- Defined in ‘GHC.Base’ -- Maybe是Functor的实例
instance Functor IO -- Defined in ‘GHC.Base’ -- IO是Functor的实例
-- 2. 查看类型类的官方文档(需联网,简洁易懂)
ghci> :doc Functor
-- 输出:Functor类表示“可映射的容器”,fmap用于将函数应用到容器内的所有值,保持容器结构不变...
-- 3. 查看某个类型属于哪些类型类(快速了解类型的可用行为)
-- 示例:查看Int类型的类型类归属
ghci> :info Int
data Int = GHC.Types.I# GHC.Prim.Int#
-- Defined in ‘GHC.Types’
instance Bounded Int -- Int属于Bounded类(有上下界)
instance Enum Int -- Int属于Enum类(可枚举)
instance Eq Int -- Int属于Eq类(可判断相等)
instance Integral Int -- Int属于Integral类(整数运算)
instance Num Int -- Int属于Num类(基础数值运算)
instance Ord Int -- Int属于Ord类(可比较大小)
instance Read Int -- Int属于Read类(字符串转类型)
instance Show Int -- Int属于Show类(类型转字符串)
-- 4. 查看类型类的某个方法的定义
ghci> :info (==)
(==) :: forall a. Eq a => a -> a -> Bool
-- Defined in ‘GHC.Classes’
infix 4 == -- 运算符优先级:4级
3. 离线文档(无网络时使用)
安装 Haskell 平台(或 GHC、Cabal/Stack)后,可通过命令生成本地 HTML 文档,离线浏览所有 base 包的模块和类型类,步骤如下:
-- 方法1:使用cabal生成(适合cabal用户)
cabal haddock base --html
-- 生成后,文档路径会显示在终端(通常在 ~/.cabal/share/doc/... 目录下)
-- 方法2:使用stack生成(适合stack用户,推荐)
stack haddock base
-- 生成后,可通过命令 stack haddock --open base 直接用浏览器打开文档
核心优势:无网络依赖,文档结构和在线官网一致,可快速搜索类型类、模块,适合离线学习或无网络环境开发。
三、标准库核心类型类全分类解析(含模块+示例)
base 包中的类型类按“功能场景”可分为8大类,每类均包含「模块归属、类型类清单、核心方法、用途、典型实例、实战示例」,确保学完就能用,分类逻辑贴合新手学习顺序:
1. 基础行为类(Prelude 默认加载,必学)
模块归属:核心来自 Prelude 模块(默认预加载),底层定义在Data.Eq、Data.Ord 等模块,无需手动导入,覆盖“值的基础操作”,是所有其他类型类的基础。
| 类型类 | 核心方法 | 核心用途 | 典型实例 | 实战示例 |
|---|---|---|---|---|
Eq | (==):判断相等(/=):判断不相等 | 判断两个同类型值是否相等,是所有可比较类型的基础 | Int、String、Maybe a、[a] | 1 == 2 -- 输出 False `` "abc" /= "def" -- 输出 True `` Just 5 == Just 5-- 输出 True ``[1,2] == [3,4] -- 输出 False |
Ord | compare:返回比较结果<、<=、>、>=:比较大小 | 大小比较、排序、查找,所有 Eq 实例均可实现 Ord | Int、String、Bool、Maybe a | 5 `compare` 3 -- 输出 GT(Greater Than,大于) ```` 3 `compare` 5 -- 输出 LT(Less Than,小于) ``` sort [3,1,2] -- 输出 [1,2,3](依赖Ord类) "apple" < "banana" -- 输出 True(字符串按字典序比较)` |
Show | show:类型转字符串showList:列表转字符串(默认实现) | 类型序列化(将值转为人类可读的字符串),用于打印、日志输出 | 几乎所有标准类型(Int、Bool、Maybe a、[a]) | show 123 -- 输出 "123"(字符串类型) `` show True -- 输出 "True" `` show (Just 456) -- 输出 "Just 456" ``show [1,2,3] -- 输出 "[1,2,3]" |
Read | readsPrec:字符串转类型read:简化版字符串转类型(常用) | 类型反序列化(将字符串转为指定类型的值),用于解析输入 | 几乎所有标准类型(Int、Bool、Maybe a) | read "123" :: Int -- 输出 123(需指定目标类型) `` read "True" :: Bool -- 输出 True `` read "Just 5" :: Maybe Int -- 输出 Just 5 ``read "[1,2,3]" :: [Int] -- 输出 [1,2,3] |
Enum | succ:取下一个值pred:取上一个值enumFrom:生成连续值 | 枚举类型(生成连续值、遍历连续范围),适合循环、序列生成 | Int、Char、Bool | succ 5 -- 输出 6 `` pred 5 -- 输出 4 `` enumFrom 1 :: [Int] -- 输出 [1,2,3,...](无限序列) ``['a'..'z'] -- 输出 "abcdefghijklmnopqrstuvwxyz"(依赖Enum类) |
Bounded | minBound:取类型最小值maxBound:取类型最大值 | 获取有上下界类型的最值,适合边界判断、范围限制 | Int、Bool、Char(无无限类型) | minBound :: Int -- 输出 -9223372036854775808(64位系统Int最小值) `` maxBound :: Int -- 输出 9223372036854775807(64位系统Int最大值) `` minBound :: Bool -- 输出 False ``maxBound :: Bool -- 输出 True |
2. 数值运算类(Numeric 模块体系,处理数字)
模块归属:核心来自 Numeric.Num、Numeric.Fractional 等模块,默认通过 Prelude 导入,无需手动导入,专门用于数值计算,区分“整数、浮点数、分数”的运算规则,避免运算错误。
| 类型类 | 核心方法 | 核心用途 | 典型实例 | 实战示例 |
|---|---|---|---|---|
Num | (+):加法(-):减法(*):乘法fromInteger:整数转当前类型 | 基础数值运算,是所有数值类型的基础,不包含除法(避免整数/浮点数混淆) | Int、Double、Float、Integer(任意精度整数) | 1 + 2 * 3 -- 输出 7(遵循运算优先级) `` 5 - 3 -- 输出 2 `` 4 * 6 -- 输出 24 ``fromInteger 5 :: Double -- 输出 5.0(整数转浮点数) |
Fractional | (/):除法fromRational:有理数转当前类型 | 分数运算(除法返回浮点数或分数),解决 Num 类无除法的问题 | Double、Float、Rational(精确分数) | 10 / 3 -- 输出 3.3333333333333335(Double类型) `` fromRational (3/2) :: Double -- 输出 1.5 `` -- 注意:Int不属于Fractional,无法使用(/),需先转类型 ``fromIntegral 10 / fromIntegral 3 :: Double -- 输出 3.333... |
Floating | sin:正弦cos:余弦exp:自然指数log:自然对数pi:圆周率常量 | 浮点数专用运算(三角函数、指数、对数),仅适用于浮点数类型 | Double、Float | sin (pi / 2) -- 输出 1.0(正弦函数,pi是Floating类常量) `` cos pi -- 输出 -1.0(余弦函数) `` exp 1 -- 输出 2.718281828459045(自然指数e的1次方) ``log 10 -- 输出 2.302585092994046(自然对数) |
Integral | div:整数整除mod:整数取模toInteger:当前类型转整数 | 整数专用运算(整除、取模),解决“整数除法取整”的需求 | Int、Integer | `` 10 div 3 -- 输出 3(整数整除,向下取整) 10 `mod` 3 -- 输出 1(整数取模,余数) (-10) div 3 -- 输出 -4(Haskell中整除向下取整) ```toInteger 10 :: Integer -- 输出 10(Int转任意精度整数)` |
Real | toRational:当前类型转有理数 | 连接整数和分数类型,实现整数、浮点数、分数之间的转换,保证精度 | Int、Double、Rational | toRational 5 :: Rational -- 输出 5 % 1(有理数,分子/分母) `` toRational 3.5 :: Rational -- 输出 7 % 2(精确表示3.5) ```toRational (10div` 3) :: Rational -- 输出 3 % 1`` |
RealFrac | properFraction:拆分实数为整数+小数部分truncate:截断取整round:四舍五入取整 | 实数拆分、取整,适用于需要分离整数和小数部分的场景 | Double、Float、Rational | properFraction 3.5 :: (Int, Double) -- 输出 (3, 0.5) `` truncate 3.9 :: Int -- 输出 3(截断取整) `` round 3.5 :: Int -- 输出 4(四舍五入) ``round 3.4 :: Int -- 输出 3 |
RealFloat | floatRadix:浮点数基数exponent:浮点数指数部分significand:浮点数尾数部分 | 浮点数底层操作,用于获取浮点数的底层结构(基数、指数、尾数),适合底层数值处理 | Double、Float | floatRadix (1.0 :: Double) -- 输出 2(Double是二进制浮点数) `` exponent (8.0 :: Double) -- 输出 3(8.0 = 1.0 * 2^3) ``significand (8.0 :: Double) -- 输出 1.0(尾数部分) |
3. 函子类(Functor 体系,处理“容器”)
模块归属:核心来自 Data.Functor、Control.Applicative、Control.Monad 模块,默认通过 Prelude 导入核心方法,无需手动导入,是 Haskell 的核心抽象,用于处理“带上下文的值(容器)”的映射和计算,是 Monad 的基础。
| 类型类 | 核心方法 | 核心用途 | 典型实例 | 实战示例 | |||||||
|---|---|---|---|---|---|---|---|---|---|---|---|
Functor | fmap:映射容器内的值<$>:fmap 的中缀形式(常用)<$:将值替换容器内所有元素 | 单参数容器映射(无副作用),将函数应用到容器内的所有值,保持容器结构不变 | [](列表)、Maybe(可选值)、IO(IO操作)、Either e(错误处理) | -- 列表容器 `` fmap (+1) [1,2,3] -- 输出 [2,3,4] `` (+1) <$> [1,2,3] -- 输出 [2,3,4](和fmap等价,更简洁) `` 0 <$ [1,2,3] -- 输出 [0,0,0](替换所有元素) ```` -- Maybe容器 `` fmap (*2) (Just 5) -- 输出 Just 10 `` fmap (*2) Nothing -- 输出 Nothing(空容器,无变化) ```` -- Either容器(Right是有效容器,Left是错误) `` fmap not (Right True) -- 输出 Right False ``fmap not (Left "err") -- 输出 Left "err"(错误容器,无变化) | |||||||
Contravariant | contramap:反向映射(函数参数反向) | 逆变函子(反向映射),适用于“消费值”的容器(如谓词、比较函数),和 Functor 方向相反 | Predicate a(谓词:a->Bool)、Comparison a(比较:a->a->Ordering) | -- 导入Contravariant模块 `` import Data.Functor.Contravariant ```` -- 谓词容器:判断Int是否大于3 `` predicate :: Predicate Int `` predicate = Predicate (>3) ```` -- 反向映射:将Int转为String,判断String长度是否大于3 `` newPred = contramap length predicate `` getPredicate newPred "abc" -- 输出 False(长度3,不大于3) ``getPredicate newPred "abcd" -- 输出 True(长度4,大于3) | |||||||
Applicative | pure:将值包装成容器<*>:应用容器内的函数到容器内的值*>:忽略第一个容器的值,保留第二个<*:忽略第二个容器的值,保留第一个 | 多参数容器映射(参数也在容器中),解决 Functor 无法处理“多参数函数+多容器”的问题 | []、Maybe、IO、(->) r(函数容器) | -- Maybe容器(多参数映射) `` pure (+) <*> Just 2 <*> Just 3 -- 输出 Just 5 `` pure (+) <*> Just 2 <*> Nothing -- 输出 Nothing ```` -- 列表容器(多参数映射,所有组合) `` pure (+) <*> [1,2] <*> [3,4] -- 输出 [4,5,5,6] `` [(*2), (+3)] <*> [1,2] -- 输出 [2,4,4,5] ```` -- IO容器(IO函数应用到IO值) ``pure (++) <*> getLine <*> getLine -- 读取两个输入,拼接后输出 | |||||||
Alternative | empty:空容器(单位元)`< | >`:容器选择(取第一个非空容器) | Applicative 的“可选/多值”扩展,用于“选择第一个有效容器”“组合多个可选值” | []、Maybe、IO(极少用) | ` -- Maybe容器(选择第一个非空值) `` Just 5 < | > Just 6 -- 输出 Just 5 `` Nothing < | > Just 6 -- 输出 Just 6 `` Nothing < | > Nothing -- 输出 Nothing(empty) ```` -- 列表容器(拼接列表,作为选择的扩展) `` [1,2] < | > [3,4] -- 输出 [1,2,3,4] `` empty < | > [3,4] -- 输出 [3,4](empty是[]) ```` -- 实际用途:获取第一个有效值 ``lookup "a" [("b",2), ("a",1)] < | > lookup "a" [("a",3)] -- 输出 Just 1` |
Monad | (>>=):绑定操作(将容器内的值传递给返回容器的函数)return:和 pure 等价(包装值成容器)>>:忽略前一个容器的值,执行后一个容器 | 单子(顺序依赖计算),解决 Applicative 无法处理“函数返回容器、依赖前一个容器值”的问题,是 Haskell 处理副作用、顺序计算的核心 | []、Maybe、IO、Either e | -- Maybe容器(依赖前一个值的计算) `` Just 5 >>= \x -> Just (x * 2) -- 输出 Just 10 `` Just 5 >>= \x -> if x > 3 then Just (x+1) else Nothing -- 输出 Just 6 `` Nothing >>= \x -> Just (x*2) -- 输出 Nothing ```` -- IO容器(顺序依赖的IO操作,最常用) `` main = do `` putStrLn "输入你的名字:" -- IO操作1:打印提示 `` name <- getLine -- IO操作2:读取输入,依赖操作1执行 `` putStrLn $ "你好," ++ name -- IO操作3:打印结果,依赖操作2的name ```` -- 列表容器(所有可能的依赖组合) ``[1,2] >>= \x -> [x, x*2] -- 输出 [1,2,2,4] | |||||||
MonadPlus | mzero:和 empty 等价(空容器)mplus:和 `< | >` 等价(容器选择/拼接) | Monad 的“可选/多值”扩展,结合 Monad 的顺序计算和 Alternative 的选择功能 | []、Maybe | -- Maybe容器 `` mzero :: Maybe Int -- 输出 Nothing ``` Just 5mplusJust 6 -- 输出 Just 5 ```` NothingmplusJust 6 -- 输出 Just 6 ````` -- 列表容器 `` mzero :: [Int] -- 输出 [] ``` [1,2]mplus [3,4] -- 输出 [1,2,3,4] ````` -- 结合Monad的顺序计算 ``[1,2] >>= \x -> if x > 1 then [x] else mzero -- 输出 [2] | ||||||
MonadIO | liftIO:将 IO 操作嵌入到其他 Monad 中 | 解决“非 IO Monad 中执行 IO 操作”的问题(如 ReaderT、StateT等复合 Monad) | IO、ReaderT r IO、StateT s IO、ExceptT e IO | -- 导入相关模块 `` import Control.Monad.Reader `` import Control.Monad.IO.Class ```` -- 定义ReaderT r IO Monad(带只读环境的IO) `` printEnv :: ReaderT String IO () `` printEnv = do `` env <- ask -- 获取只读环境(String类型) `` liftIO $ putStrLn $ "当前环境:" ++ env -- 嵌入IO操作 ```` -- 运行ReaderT Monad ``runReaderT printEnv "production" -- 输出 "当前环境:production" | |||||||
MonadReader | ask:获取只读环境local:临时修改只读环境,执行函数后恢复reader:将环境映射函数包装成 Monad | 带只读环境的 Monad,用于“多个函数需要共享同一个只读配置”的场景(如配置文件、日志级别) | Reader r、ReaderT r m(复合 Monad) | -- 导入模块 `` import Control.Monad.Reader ```` -- 定义带只读环境(Int类型,代表日志级别)的计算 `` logInfo :: String -> Reader Int () `` logInfo msg = do `` level <- ask -- 获取日志级别(1=INFO,2=DEBUG) `` if level >= 1 then putStrLn $ "INFO: " ++ msg else return () ```` logDebug :: String -> Reader Int () `` logDebug msg = do `` level <- ask `` if level >= 2 then putStrLn $ "DEBUG: " ++ msg else return () ```` -- 运行Reader Monad(日志级别=2,显示INFO和DEBUG) `` runReader (logInfo "启动程序" >> logDebug "加载配置") 2 `` -- 输出: `` -- INFO: 启动程序 `` -- DEBUG: 加载配置 ```` -- local临时修改环境(日志级别=1,只显示INFO) `` runReader (logInfo "启动程序" >> local (_ -> 1) (logDebug "加载配置")) 2 ``-- 输出:INFO: 启动程序 | |||||||
MonadWriter | tell:写入日志(追加到日志容器)listen:执行计算,获取结果和日志pass:根据结果修改日志 | 带日志功能的 Monad,用于“计算过程中需要记录日志”的场景,日志自动追加,无需手动传递 | Writer w、WriterT w m(复合 Monad),w 需是 Monoid 实例 | -- 导入模块 `` import Control.Monad.Writer `` import Data.Monoid (Sum(..)) ```` -- 定义带日志(Sum Int,记录操作次数)的计算 `` add :: Int -> Int -> Writer (Sum Int) Int `` add x y = do `` tell (Sum 1) -- 写入日志:操作次数+1 `` return (x + y) ```` -- 运行Writer Monad,获取结果和日志 `` let (result, log) = runWriter (add 1 2 >> add 3 4) `` result -- 输出 7(最后一次计算结果) `` getSum log -- 输出 2(两次操作,日志累计) ```` -- listen:获取计算结果和日志 ``listen (add 1 2) -- 输出 (3, Sum 1) | |||||||
MonadState | get:获取当前状态put:设置新状态modify:修改当前状态(应用函数)state:将状态映射函数包装成 Monad | 带状态的 Monad,用于“计算过程中需要维护一个可变状态”的场景(如计数器、缓存),状态自动传递 | State s、StateT s m(复合 Monad) | -- 导入模块 `` import Control.Monad.State ```` -- 定义带状态(Int类型,计数器)的计算 `` increment :: State Int Int `` increment = do `` count <- get -- 获取当前计数器状态 `` put (count + 1) -- 设置新状态:计数器+1 `` return count -- 返回修改前的状态 ```` -- 运行State Monad,获取结果和最终状态 `` runState increment 0 -- 输出 (0, 1)(返回0,最终状态1) `` runState (increment >> increment >> increment) 0 -- 输出 (1, 3) ```` -- modify:简化状态修改 `` decrement :: State Int () `` decrement = modify (\x -> x - 1) ``runState (increment >> decrement) 0 -- 输出 ((), 0)(状态回到初始值) |
4. 容器遍历/聚合类(处理集合)
模块归属:核心来自 Data.Foldable、Data.Traversable 模块,Foldable 由 Prelude 默认导入,Traversable 需手动导入,专门用于“容器的遍历和聚合”,比 Functor 更通用(支持空容器、多值容器)。
| 类型类 | 核心方法 | 核心用途 | 典型实例 | 实战示例 |
|---|---|---|---|---|
Foldable | foldMap:将容器值映射后聚合foldr:右折叠(从右到左聚合)foldl:左折叠(从左到右聚合)sum:求和(简化版 fold)length:求长度(简化版 fold)any:判断是否有元素满足条件 | 容器折叠(聚合/遍历,无副作用),将容器内的所有值聚合为一个单一值,支持所有可折叠容器(包括空容器) | []、Maybe、Tree(树)、Map k v(字典)、Set a(集合) | -- 列表容器(最常用) `` sum [1,2,3] -- 输出 6(求和,依赖Foldable) `` length [1,2,3] -- 输出 3(求长度) `` any even [1,2,3] -- 输出 True(判断是否有偶数) `` foldr (+) 0 [1,2,3] -- 输出 6(右折叠:1+(2+(3+0))) `` foldl (+) 0 [1,2,3] -- 输出 6(左折叠:((0+1)+2)+3) ```` -- Maybe容器(空容器返回聚合初始值) `` sum (Just 5) -- 输出 5 `` sum Nothing -- 输出 0(初始值) `` length (Just 5) -- 输出 1(非空容器长度为1) `` length Nothing -- 输出 0(空容器长度为0) ```` -- foldMap:映射+聚合(需Monoid实例,如String、Sum) `` foldMap show [1,2,3] :: String -- 输出 "123"(映射为字符串,聚合拼接) ``foldMap Sum [1,2,3] :: Sum Int -- 输出 Sum 6(映射为Sum,聚合求和)``Traversable``traverse:带副作用映射+遍历sequenceA:容器内副作用执行并聚合mapM:简化版traverse(IO场景常用)可遍历容器(带副作用的遍历/映射),结合Functor和Applicative,解决“容器映射+副作用”的需求[]、Maybe、Either e、Tree a`` -- 导入模块 `` import Data.Traversable ```` -- Maybe容器+IO副作用(遍历并执行IO) `` traverse print (Just 5) -- 输出 5,返回 IO (Just ()) `` traverse print Nothing -- 无输出,返回 IO Nothing ```` -- 列表容器+IO副作用(批量执行IO,聚合结果) `` mapM print [1,2,3] -- 依次输出1、2、3,返回 IO [(),(),()] `` traverse (\x -> getLine) [1,2] -- 读取两次输入,返回 IO [输入1, 输入2] ```` -- sequenceA:执行容器内的副作用 `` sequenceA [print 1, print 2] -- 依次输出1、2,返回 IO [(),()] ``sequenceA (Just (putStrLn "hello")) -- 输出hello,返回 IO (Just ()) |
5. 代数结构类(精简核心,需手动导入)
模块归属:核心来自 Data.Semigroup、Data.Monoid 模块,需手动导入,聚焦“二元运算+单位元”的抽象,是容器聚合、日志拼接等场景的基础。
| 类型类 | 核心方法 | 核心用途 | 典型实例 | 实战示例 |
|---|---|---|---|---|
Semigroup | <>:结合律二元运算 | 抽象“可结合的二元操作”,无需单位元,用于值的拼接、聚合 | String、[a]、Sum Int、Product Int | -- 导入模块 `` import Data.Semigroup ```` "hello" <> " world" -- 输出 "hello world"(字符串拼接) `` [1,2] <> [3,4] -- 输出 [1,2,3,4](列表拼接) `` Sum 2 <> Sum 3 -- 输出 Sum 5(求和拼接) ``Product 2 <> Product 3 -- 输出 Product 6(求积拼接) |
Monoid | mempty:单位元mappend:等价于<> | Semigroup的扩展(带单位元),用于“有初始值的聚合”场景 | String、[a]、Sum Int、() | -- 导入模块 `` import Data.Monoid ```` mempty :: String -- 输出 ""(字符串单位元) `` mempty :: [Int] -- 输出 [](列表单位元) ``` [1,2]mappendmempty -- 输出 [1,2] ```` Sum 5mappendmempty -- 输出 Sum 5 ````memptymappend` Product 4 -- 输出 Product 4`` |
6. 错误处理类(高频核心,精简冗余)
模块归属:核心来自 Control.Monad.Except 模块,需手动导入,聚焦“带错误信息的计算”,解决Monad中无法直观传递错误的问题。
| 类型类 | 核心方法 | 核心用途 | 典型实例 | 实战示例 | ||
|---|---|---|---|---|---|---|
MonadError e | throwError:抛出错误catchError:捕获错误并处理 | 带错误处理的Monad,用于需要传递错误信息、捕获并恢复的场景 | Except e、Either e、ExceptT e m | ` -- 导入模块 import Control.Monad.Except ```` -- Except String Int:错误为String,正常结果为Int calc :: Int -> Except String Int calc x | x == 0 = throwError "错误:除数不能为0" ``` | otherwise = return (10 div x) ````` -- 运行并处理错误 runExcept (calc 0) -- 输出 Left "错误:除数不能为0" runExcept (calc 5) -- 输出 Right 2 ```` -- 捕获错误并恢复 ```runExcept (calc 0 catchError \err -> return 0) -- 输出 Right 0`` |
四、关键补充说明
1. 学习优先级
- 基础行为类(Eq/Ord/Show/Read):入门必备,处理基础值操作;
- 数值运算类(Num/Fractional/Integral):满足日常数值计算;
- 函子类(Functor→Applicative→Monad):Haskell核心,处理容器和副作用;
- 容器遍历/聚合类(Foldable/Traversable):高频容器操作;
- 进阶:代数结构类、错误处理类(按需学习,用到再深入)。