Chapter 2. Why function purity matters
覆盖purity/side effects、concurrency、testability与computing evolution,把pure core、effect shell、property tests和parallel safety落到可复现边界。
学习目标
- 能解释pure function的explicit input、determinism与no observable side effect要求,区分读取依赖和写入effect
- 能分析shared mutation与parallel schedule的race surface,设计pure parallel work加single-owner commit
- 能设计example、property、substitution与effect-boundary tests,复现purity对testability和optimization的收益
机制总览
Chapter 2. Why function purity matters:机制路径
- 1
为什么Purity是可替换性而不是道德标签
Pure function的价值不是“看起来干净”,而是caller可只凭inputs推理output:相同input给相同result,evaluation不修改外部可观察state。于是call可以被result替换、重复/缓存/并行更容易保持语义。Impure operation不是坏代码;I/O是系统目的之一,但需要隔离和拥有者。
- 2
What is function purity?
Purity有两面:deterministic mapping与absence of side effects。Clock、random、environment variable、database read是hidden inputs;file/database write、mutation、log…
- 3
Purity and concurrency
Concurrency困难来自多个executions共享mutable state。
章级决策实验
Chapter 2. Why function purity matters:机制与证据
切换《Chapter 2. Why function purity matters》的三个关键教学阶段,先解释机制,再用运行与失败证据验证结论。
选择推理阶段
当前阶段 · 为什么Purity是可替换性而不是道德标签
Pure function的价值不是“看起来干净”,而是caller可只凭inputs推理output:相同input给相同result,evaluation不修改外部可观察state。于是call可以被result替换、重复/缓存/并行更容易保持语义。Impure operation不是坏代码;I/O是系统目的之一,但需要隔离和拥有者。
可核验证据
以确定输入重复运行「为什么Purity是可替换性而不是道德标签」的最小管线,用属性测试、状态快照和副作用调用轨迹核对返回值、失败传播与资源边界。
学完《Chapter 2. Why function purity matters》后,应能从输入和前置条件推导状态变化,并用可重复的构建、运行或边界测试证明结果。
失效—证据矩阵
Chapter 2. Why function purity matters:失效与核验
为什么Purity是可替换性而不是道德标签
典型失效
若把「为什么Purity是可替换性而不是道德标签」只写成函数式术语而不隔离副作用、状态和失败分支,组合后的程序仍会依赖隐藏时序,无法从输入稳定推导结果。
核验证据
以确定输入重复运行「为什么Purity是可替换性而不是道德标签」的最小管线,用属性测试、状态快照和副作用调用轨迹核对返回值、失败传播与资源边界。
What is function purity?
典型失效
若把「What is function purity?」只写成函数式术语而不隔离副作用、状态和失败分支,组合后的程序仍会依赖隐藏时序,无法从输入稳定推导结果。
核验证据
以确定输入重复运行「What is function purity?」的最小管线,用属性测试、状态快照和副作用调用轨迹核对返回值、失败传播与资源边界。
Purity and concurrency
典型失效
若把「Purity and concurrency」只写成函数式术语而不隔离副作用、状态和失败分支,组合后的程序仍会依赖隐藏时序,无法从输入稳定推导结果。
核验证据
以确定输入重复运行「Purity and concurrency」的最小管线,用属性测试、状态快照和副作用调用轨迹核对返回值、失败传播与资源边界。
为什么Purity是可替换性而不是道德标签
Pure function的价值不是“看起来干净”,而是caller可只凭inputs推理output:相同input给相同result,evaluation不修改外部可观察state。于是call可以被result替换、重复/缓存/并行更容易保持语义。Impure operation不是坏代码;I/O是系统目的之一,但需要隔离和拥有者。
先预测:读取immutable global config是否pure;写logger是否可忽略;抛deterministic argument exception是否算side effect;thread-safe method是否等于pure;pure function是否一定快。答案都需要按observable contract与environment边界具体分析。
↡Result只取决于显式inputs,evaluation不改变外部可观察state,因此call可被result替换。What is function purity?
Purity有两面:deterministic mapping与absence of side effects。Clock、random、environment variable、database read是hidden inputs;file/database write、mutation、logging是effects。Exception也影响totality:若对domain内input可能throw,signature未表达所有outcomes;但固定invalid argument总抛同一failure仍比依赖性fault更易推理。
实用做法是functional core / imperative shell。Shell读取clock、store、HTTP,把facts作为values传给core;core返回decision/commands;shell解释commands并拥有retry、transaction与logging。Effect没有消失,只是从business calculation里移到visible boundary。
public static Discount DecideDiscount(Customer customer, DateTimeOffset now) =>
customer.IsVip && customer.MembershipExpiresAt >= now
? new Discount(0.20m)
: Discount.None;切换pure、clock、write与random
Purity and concurrency
Concurrency困难来自多个executions共享mutable state。counter++是read-modify-write,interleaving会丢update;加lock可保证正确但引入contention和ordering。若每个item transformation只读immutable input并返回result,scheduler改变顺序不会改变per-item mapping,parallelization更安全。
最终系统仍要aggregate/persist。策略是parallel pure compute,single owner/reducer commit;或者每个worker返回immutable partial result,最后用associative combine。是否可parallel还依赖operation laws、ordering与resource limits,不是看到pure就无限开threads。
var partials = orders
.AsParallel()
.Select(order => CalculateInvoice(order))
.ToArray();
var batch = partials.Aggregate(InvoiceBatch.Empty, InvoiceBatch.Combine);切换shared counter、pure map、ordered effect与cache
Purity and testability
Pure function测试只需构造value并assert result,无数据库fixture、clock patch或mock order。更重要的是可以做property tests:sort保持multiset且有序,normalize idempotent,combine满足associativity。Test描述laws,覆盖比几个examples更宽。
Impure shell也要test,但测试目标不同:given pure decision,是否发送正确command、transaction是否收敛、fault/cancel是否cleanup。Fake dependency在boundary记录commands,而不是mock每个private call。这样core tests快而稳定,integration tests集中于真正effects。
[Property]
public void Normalize_is_idempotent(CustomerName input)
{
Assert.Equal(Normalize(input), Normalize(Normalize(input)));
}切换example、property、effect shell与refactor
Purity and the evolution of computing
更多cores、distributed services和retry-heavy systems放大shared state成本。Pure/value-oriented code更容易vectorize、parallelize、cache、replay和move across process,因为它把dependency和result变成data。Cloud/distributed并不自动要求FP,但deterministic decision与explicit command对idempotency、event replay和failure recovery尤其重要。
Modern hardware optimization仍需measure;immutability可能增加allocation,persistent data structure有不同cache behavior。Purity提供semantic freedom,runtime是否利用这份自由取决于representation、compiler/JIT和workload。
Semantic freedom is not a cost model
同一个pure mapping可用eager array、lazy iterator、SIMD或parallel worker实现,observable contract相同但allocation、latency和ordering成本不同。先用purity获得可替换性,再以真实数据选择representation;不要把“更容易优化”误写成“当前实现已经最优”。
↡在不改变observable result的前提下安全执行cache、parallel、reorder或replay所需的semantic余地。本章回顾:把可变世界包在可替换计算外面
- Purity要求explicit inputs、deterministic result和no observable side effects。
- Clock/read/write/logging分别是hidden dependency或effect,应由shell拥有。
- Pure per-item compute缩小parallel schedule surface;aggregate/commit仍需order与owner。
- Pure core适合example、property与substitution tests,effect shell适合protocol/fault tests。
- Referential transparency提供optimization freedom,不自动保证performance。
练习
问题 1:读取只读configuration singleton为什么仍可能不pure?
问题 2:如何parallelize订单计算且保持一次性写库?
问题 3:一个purity refactor最有价值的property test是什么?
术语表
名词解释
本章出现的专业名词,用大白话再讲一遍。
- referential transparency
- pure core effect shell
- property-based law
- optimization freedom
原版目录概念补充核对
以下条目补齐官方目录中容易被示例主线掩盖的概念。它们不重复罗列目录,而是明确每项概念的机制、适用边界和验收证据。
Purity and concurrency:机制、边界与证据
Chapter 2. Why function purity matters中的Purity and concurrency应写成可组合的输入—输出契约,并把环境读取、状态改变与失败显式放在边界。用确定输入运行正常、空值和失败样本,同时记录返回值与副作用轨迹,证明结论不依赖隐藏状态。
Purity and testability:机制、边界与证据
Chapter 2. Why function purity matters中的Purity and testability应写成可组合的输入—输出契约,并把环境读取、状态改变与失败显式放在边界。用确定输入运行正常、空值和失败样本,同时记录返回值与副作用轨迹,证明结论不依赖隐藏状态。
Purity and the evolution of computing:机制、边界与证据
Chapter 2. Why function purity matters中的Purity and the evolution of computing应写成可组合的输入—输出契约,并把环境读取、状态改变与失败显式放在边界。用确定输入运行正常、空值和失败样本,同时记录返回值与副作用轨迹,证明结论不依赖隐藏状态。