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. 1

    为什么Purity是可替换性而不是道德标签

    Pure function的价值不是“看起来干净”,而是caller可只凭inputs推理output:相同input给相同result,evaluation不修改外部可观察state。于是call可以被result替换、重复/缓存/并行更容易保持语义。Impure operation不是坏代码;I/O是系统目的之一,但需要隔离和拥有者。

  2. 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. 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边界具体分析。

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;
分步1 / 3

切换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);
分步1 / 3

切换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)));
}
分步1 / 3

切换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;不要把“更容易优化”误写成“当前实现已经最优”。

本章回顾:把可变世界包在可替换计算外面

  1. Purity要求explicit inputs、deterministic result和no observable side effects。
  2. Clock/read/write/logging分别是hidden dependency或effect,应由shell拥有。
  3. Pure per-item compute缩小parallel schedule surface;aggregate/commit仍需order与owner。
  4. Pure core适合example、property与substitution tests,effect shell适合protocol/fault tests。
  5. Referential transparency提供optimization freedom,不自动保证performance。

练习

问题 1:读取只读configuration singleton为什么仍可能不pure?

问题 2:如何parallelize订单计算且保持一次性写库?

问题 3:一个purity refactor最有价值的property test是什么?

术语表

名词解释

本章出现的专业名词,用大白话再讲一遍。

referential transparency
pure core effect shell
shared-mutation schedule
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应写成可组合的输入—输出契约,并把环境读取、状态改变与失败显式放在边界。用确定输入运行正常、空值和失败样本,同时记录返回值与副作用轨迹,证明结论不依赖隐藏状态。

讨论

评论区加载中…