第7章:Haskell(纯函数式)
一、导读
1.1 本章学习目标
第7章介绍 Haskell——一门纯函数式语言。Haskell 将函数式编程推向极致:没有可变状态、没有副作用、所有函数都是纯函数。
本章的核心学习目标包括:
- 理解纯函数式编程:没有副作用、引用透明
- 掌握类型类(Type Class):Haskell 的多态机制
- 理解 Monad:如何在纯函数式中处理副作用
- 体验惰性求值:表达式只在需要时才计算
1.2 为什么本章如此重要
Haskell 是函数式编程的"教科书"。它的设计影响了 Swift、Kotlin、Rust 等众多现代语言。理解 Haskell 的类型系统、Monad、惰性求值,能让你更深刻地理解函数式编程的本质。很多编程语言的高级特性——泛型、模式匹配、代数数据类型——都能在 Haskell 中找到最纯粹的形态。
1.3 核心问题引导
纯函数式编程没有副作用,那它如何与现实世界交互?
什么是 Monad?为什么它被称为"函数式编程的终极抽象"?
类型类和 Java 的接口有什么异同?
惰性求值有什么优势和劣势?
二、核心概念详解
2.1 纯函数
haskell
-- 纯函数:相同输入总是产生相同输出,没有副作用
add :: Int -> Int -> Int
add x y = x + y
-- 高阶函数
applyTwice :: (a -> a) -> a -> a
applyTwice f x = f (f x)
-- 模式匹配
factorial :: Integer -> Integer
factorial 0 = 1
factorial n = n * factorial (n - 1)
-- 守卫
abs' :: Int -> Int
abs' n
| n < 0 = -n
| otherwise = n2.2 类型类
haskell
-- 类型类定义接口
class Eq a where
(==) :: a -> a -> Bool
(/=) :: a -> a -> Bool
x /= y = not (x == y)
-- 实例
instance Eq Int where
x == y = primEqInt x y
-- 带约束的函数
elem :: (Eq a) => a -> [a] -> Bool
elem _ [] = False
elem x (y:ys) = x == y || elem x ys
-- 常用类型类
-- Num: 数字操作
-- Ord: 有序比较
-- Show: 转为字符串
-- Read: 从字符串解析
-- Functor: 可映射
-- Monad: 可绑定2.3 代数数据类型
haskell
-- 代数数据类型
data Shape = Circle Double | Rectangle Double Double
deriving (Show)
area :: Shape -> Double
area (Circle r) = pi * r * r
area (Rectangle w h) = w * h
-- Maybe 类型
data Maybe a = Nothing | Just a
deriving (Show)
safeDiv :: Double -> Double -> Maybe Double
safeDiv _ 0 = Nothing
safeDiv x y = Just (x / y)
-- Either 类型
data Either a b = Left a | Right b2.4 Monad
haskell
-- Monad 类型类
class Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
-- Maybe 是 Monad
instance Maybe Monad where
return = Just
Nothing >>= _ = Nothing
(Just x) >>= f = f x
-- 使用 do 语法
lookupUser :: String -> Maybe User
lookupUser name = do
userId <- findUserId name
user <- getUserById userId
return user
-- IO 是 Monad
main :: IO ()
main = do
putStrLn "What is your name?"
name <- getLine
putStrLn ("Hello, " ++ name ++ "!")2.5 惰性求值
haskell
-- 惰性求值:表达式只在需要时才计算
-- 无限列表
naturals = [1..]
evens = filter even naturals
first10Evens = take 10 evens -- [2,4,6,8,10,12,14,16,18,20]
-- 斐波那契数列
fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
first20Fibs = take 20 fibs
-- 惰性求值的好处
-- 1. 可以处理无限数据结构
-- 2. 只计算需要的部分
-- 3. 可以组合操作而不创建中间结果三、实战案例
3.1 案例一:用 Monad Transformer 构建 Web 应用
haskell
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
import Control.Monad.Reader
import Control.Monad.Except
import Control.Monad.State
-- 应用类型
newtype App a = App
{ runApp :: ReaderT AppConfig (ExceptT AppError (StateT AppState IO)) a
} deriving (Functor, Applicative, Monad, MonadIO,
MonadReader AppConfig, MonadError AppError, MonadState AppState)
data AppConfig = AppConfig { dbUrl :: String, port :: Int }
data AppState = AppState { requestCount :: Int }
data AppError = NotFound | Unauthorized deriving (Show)
-- 使用
handleRequest :: String -> App String
handleRequest path = do
config <- ask
state <- get
put state { requestCount = requestCount state + 1 }
case path of
"/health" -> return "OK"
_ -> throwError NotFound四、本章小结
纯函数式:没有副作用,引用透明,易于推理。
类型类:Haskell 的多态机制,比 Java 接口更灵活。
Monad:在纯函数式中处理副作用的抽象。
惰性求值:表达式只在需要时计算,支持无限数据结构。
五、思考与练习
纯函数式编程的"没有副作用"如何与现实世界的 I/O 交互?Monad 是如何解决这个问题的?
Haskell 的类型类和 Java 的接口有什么本质区别?
惰性求值的优势是什么?它有什么潜在的性能问题?