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的收益

为什么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

讨论

评论区加载中…