Chapter 7. Structuring an application with functions
覆盖partial application、method resolution、currying、partial-friendly API、application modularization与fold,把dependency wiring变成typed function construction。
学习目标
- 能区分partial application与currying,推导固定stable dependencies后得到的workflow signature
- 能分析C# overload/method-group inference与parameter order,设计partial-application-friendly API
- 能设计composition root、module functions和Fold reduction,复现empty/list/state aggregation semantics
机制总览
Chapter 7. Structuring an application with functions:机制路径
- 1
为什么Application Structure可以是Fu…
传统application用mutable service objects保存dependencies;functional approach也需要dependency wiring,只是把dependencies作为parameters,再在startup部分应用,得到只接request的func…
- 2
Partial application: supplyin…
给 (Config, Store, Request) - Response 先传Config/Store,得到 Request - Response 。稳定dependencies在composition root固定,变化data在runtime传入。Closure保存dependency ref…
- 3
Overcoming the quirks of meth…
C method group不是独立value,需target delegate决定overload/generic inference。Partial helper遇overloaded method时可能ambiguous;用显式 Func 、adapter lambda或domain-name…
章级决策实验
Chapter 7. Structuring an application with functions:机制与证据
切换《Chapter 7. Structuring an application with functions》的三个关键教学阶段,先解释机制,再用运行与失败证据验证结论。
选择推理阶段
当前阶段 · 为什么Application Structure可以是Fu…
传统application用mutable service objects保存dependencies;functional approach也需要dependency wiring,只是把dependencies作为parameters,再在startup部分应用,得到只接request的func…
可核验证据
以确定输入重复运行「为什么Application Structure可以是Fu…」的最小管线,用属性测试、状态快照和副作用调用轨迹核对返回值、失败传播与资源边界。
学完《Chapter 7. Structuring an application with functions》后,应能从输入和前置条件推导状态变化,并用可重复的构建、运行或边界测试证明结果。
失效—证据矩阵
Chapter 7. Structuring an application with functions:失效与核验
为什么Application Structure可以是Fu…
典型失效
若把「为什么Application Structure可以是Fu…」只写成函数式术语而不隔离副作用、状态和失败分支,组合后的程序仍会依赖隐藏时序,无法从输入稳定推导结果。
核验证据
以确定输入重复运行「为什么Application Structure可以是Fu…」的最小管线,用属性测试、状态快照和副作用调用轨迹核对返回值、失败传播与资源边界。
Partial application: supplyin…
典型失效
若把「Partial application: supplyin…」只写成函数式术语而不隔离副作用、状态和失败分支,组合后的程序仍会依赖隐藏时序,无法从输入稳定推导结果。
核验证据
以确定输入重复运行「Partial application: supplyin…」的最小管线,用属性测试、状态快照和副作用调用轨迹核对返回值、失败传播与资源边界。
Overcoming the quirks of meth…
典型失效
若把「Overcoming the quirks of meth…」只写成函数式术语而不隔离副作用、状态和失败分支,组合后的程序仍会依赖隐藏时序,无法从输入稳定推导结果。
核验证据
以确定输入重复运行「Overcoming the quirks of meth…」的最小管线,用属性测试、状态快照和副作用调用轨迹核对返回值、失败传播与资源边界。
为什么Application Structure可以是Function Construction
传统application用mutable service objects保存dependencies;functional approach也需要dependency wiring,只是把dependencies作为parameters,再在startup部分应用,得到只接request的function。最终handler仍可交给framework,core无需service locator。
先预测:partial application是否等于currying;任何method group都可自动partial;dependency放最后是否一样好;closure捕获singleton是否没有lifetime;Aggregate无seed是否总安全。答案都是否。
↡固定multi-argument function的一部分arguments,产生接收剩余arguments的新function。Partial application: supplying arguments piecemeal
给(Config, Store, Request) -> Response先传Config/Store,得到Request -> Response。稳定dependencies在composition root固定,变化data在runtime传入。Closure保存dependency references,因此lifetime必须不短于result function;不要把request-scoped resource捕获进singleton handler。
Func<Request, Response> BuildHandler(Config config, IStore store) =>
request => Handle(config, store, request);切换partial、currying、method group与closure
Overcoming the quirks of method resolution
C# method group不是独立value,需target delegate决定overload/generic inference。Partial helper遇overloaded method时可能ambiguous;用显式Func<...>、adapter lambda或domain-named wrapper固定signature。不要用大量casts掩盖API shape不清。
新增overload可能让原本可编译的method group在rebuild后变得ambiguous;参数改名也会破坏named arguments。对公共composition boundary定义稳定delegate alias和named adapter,并用compile fixture覆盖旧调用形状,API演进时先证明resolution仍唯一。
↡Method group需要target delegate、overload与generic inference共同确定唯一callable signature的边界。Curried functions: optimized for partial application
Currying把(A,B,C)->R转换为A->B->C->R,每次application自然返回下一function。Partial application是使用行为,currying是function representation transformation。C#原生method通常uncurried,library helper可Curry/Apply;过度nested Func会损害debug/readability。
Func<TaxPolicy, Func<Currency, Func<Order, Price>>> price =
tax => currency => order => Price.Calculate(order, tax, currency);
Func<Order, Price> priceUsd = price(usTax)(Currency.Usd);Creating a partial-application-friendly API
参数顺序优先stable dependency/config,再放per-call data。避免optional flags和同type primitive;给overload提供one canonical function shape。Async/cancellation通常属于per-call context,不能在startup固定一个过期token。
切换dependency-first、data-first、overload与named function
Modularizing and composing an application
Module可以暴露builders/functions:ValidationModule.Build(config)、Workflow.Build(validate, decide, execute)。每个module返回最小delegate contract,composition root集中选择implementations。Domain workflow不依赖container APIs,test传入pure/fake functions。
var validate = Validation.Build(rules);
var decide = Pricing.Build(taxPolicy);
var execute = Execution.Build(storeFactory, clock);
var handler = Workflow.Build(validate, decide, execute);Reducing a list to a single value
Fold/Aggregate以seed和step把sequence归约为一个value。Seed定义empty result与accumulator type;step应明确ordering、associativity与effect。Sum、state replay、function composition都可视为fold。无seed Aggregate对empty partial,不适合需要total behavior的public workflow。
切换sum、state、compose与empty
本章回顾:在Startup构造Behavior,在Runtime提供Data
- Partial application固定部分arguments;currying改变multi-arg representation。
- C# method groups需target type,overload ambiguity用adapter/canonical signature解决。
- Dependency-first order让composition root自然构造request handlers。
- Captured dependencies必须满足function lifetime,scoped resources使用factory/per-call owner。
- Fold以seed和step表达total reduction及其laws。
练习
问题 1:为什么不应在singleton handler中capture DbContext?
问题 2:Overloaded Parse无法传给Map时如何修?
问题 3:Event fold为什么必须有initial state?
术语表
名词解释
本章出现的专业名词,用大白话再讲一遍。
- partial application
- method-resolution boundary
- curried function shape
- functional composition root
- fold reduction
原版目录概念补充核对
以下条目补齐官方目录中容易被示例主线掩盖的概念。它们不重复罗列目录,而是明确每项概念的机制、适用边界和验收证据。
Overcoming the quirks of method resolution:机制、边界与证据
Chapter 7. Structuring an application with functions中的Overcoming the quirks of method resolution应写成可组合的输入—输出契约,并把环境读取、状态改变与失败显式放在边界。用确定输入运行正常、空值和失败样本,同时记录返回值与副作用轨迹,证明结论不依赖隐藏状态。
Creating a partial-application-friendly API:机制、边界与证据
Chapter 7. Structuring an application with functions中的Creating a partial-application-friendly API应写成可组合的输入—输出契约,并把环境读取、状态改变与失败显式放在边界。用确定输入运行正常、空值和失败样本,同时记录返回值与副作用轨迹,证明结论不依赖隐藏状态。
Modularizing and composing an application:机制、边界与证据
Chapter 7. Structuring an application with functions中的Modularizing and composing an application应写成可组合的输入—输出契约,并把环境读取、状态改变与失败显式放在边界。用确定输入运行正常、空值和失败样本,同时记录返回值与副作用轨迹,证明结论不依赖隐藏状态。
Reducing a list to a single value:机制、边界与证据
Chapter 7. Structuring an application with functions中的Reducing a list to a single value应写成可组合的输入—输出契约,并把环境读取、状态改变与失败显式放在边界。用确定输入运行正常、空值和失败样本,同时记录返回值与副作用轨迹,证明结论不依赖隐藏状态。