Chapter 8. Working effectively with multi-argument functions

覆盖elevated function application、functor/applicative/monad、monad laws、LINQ composition与Bind/Apply选择,处理多个contextual inputs。

学习目标

  • 能推导Map、Apply、Bind、Traverse在plain/elevated function与values之间的type shape
  • 能解释functor、applicative与monad laws的refactoring意义,设计property tests验证custom context
  • 能判断independent/dependent/mixed effects选择Apply、Bind、LINQ或explicit staging

机制总览

Chapter 8. Working effectively with multi-argument functions:机制路径

  1. 1

    为什么Multi-Argument Function遇到C…

    普通 Func 接plain A/B;现实中inputs常是Option A、Validation B、Task A。先unwrap再if会重复control flow。Applicative Apply把elevated function用于elevated values,Bind处理later …

  2. 2

    Function application in the e…

    Map处理一个elevated value;Apply处理elevated function和elevated argument,多次Apply可组合multi-arg function。Independent field validations可全部运行并积累error。Bind适合functio…

  3. 3

    Functors, applicatives, monads

    Functor提供Map;applicative增加pure/lift与Apply;monad增加Bind。它们是能力层级和laws,不是class inheritance要求。一个context可以有Map/Apply/Bind,但若实现不满足laws,重构/组合会改变semantics。

先按顺序建立机制,再进入实验切换阶段并检查失效证据。

章级决策实验

Chapter 8. Working effectively with multi-argument functions:机制与证据

切换《Chapter 8. Working effectively with multi-argument functions》的三个关键教学阶段,先解释机制,再用运行与失败证据验证结论。

选择推理阶段

当前阶段 · 为什么Multi-Argument Function遇到C…

普通 Func 接plain A/B;现实中inputs常是Option A、Validation B、Task A。先unwrap再if会重复control flow。Applicative Apply把elevated function用于elevated values,Bind处理later …

可核验证据

以确定输入重复运行「为什么Multi-Argument Function遇到C…」的最小管线,用属性测试、状态快照和副作用调用轨迹核对返回值、失败传播与资源边界。

学完《Chapter 8. Working effectively with multi-argument functions》后,应能从输入和前置条件推导状态变化,并用可重复的构建、运行或边界测试证明结果。

失效—证据矩阵

Chapter 8. Working effectively with multi-argument functions:失效与核验

为什么Multi-Argument Function遇到C…

典型失效

若把「为什么Multi-Argument Function遇到C…」只写成函数式术语而不隔离副作用、状态和失败分支,组合后的程序仍会依赖隐藏时序,无法从输入稳定推导结果。

核验证据

以确定输入重复运行「为什么Multi-Argument Function遇到C…」的最小管线,用属性测试、状态快照和副作用调用轨迹核对返回值、失败传播与资源边界。

Function application in the e…

典型失效

若把「Function application in the e…」只写成函数式术语而不隔离副作用、状态和失败分支,组合后的程序仍会依赖隐藏时序,无法从输入稳定推导结果。

核验证据

以确定输入重复运行「Function application in the e…」的最小管线,用属性测试、状态快照和副作用调用轨迹核对返回值、失败传播与资源边界。

Functors, applicatives, monads

典型失效

若把「Functors, applicatives, monads」只写成函数式术语而不隔离副作用、状态和失败分支,组合后的程序仍会依赖隐藏时序,无法从输入稳定推导结果。

核验证据

以确定输入重复运行「Functors, applicatives, monads」的最小管线,用属性测试、状态快照和副作用调用轨迹核对返回值、失败传播与资源边界。

每个判断都必须能落到观测、测试或产物,不能只凭代码表面推测。

为什么Multi-Argument Function遇到Context就需要新工具

普通Func<A,B,R>接plain A/B;现实中inputs常是Option A、Validation B、Task A。先unwrap再if会重复control flow。Applicative Apply把elevated function用于elevated values,Bind处理later input依赖earlier value,Traverse翻转list/context。工具选择来自dependency graph。

先预测:两个Options组合一定用Bind吗;Apply是否可以让dependent validation工作;能写SelectMany是否证明monad laws;LINQ query syntax是否只用于IEnumerable;Task+Either是否能直接flatten为一种effect。答案都是否。

Function application in the elevated world

Map处理一个elevated value;Apply处理elevated function和elevated argument,多次Apply可组合multi-arg function。Independent field validations可全部运行并积累error。Bind适合function本身返回context且依赖前值;Traverse把多个contextual computations聚合成一个contextual list。

Validation<Func<Name, Email, Customer>> create = Valid(Customer.Create);
Validation<Customer> customer = create
    .Apply(ValidateName(dto.Name))
    .Apply(ValidateEmail(dto.Email));
分步1 / 3

切换Map、Apply、Bind与Traverse

Functors, applicatives, monads

Functor提供Map;applicative增加pure/lift与Apply;monad增加Bind。它们是能力层级和laws,不是class inheritance要求。一个context可以有Map/Apply/Bind,但若实现不满足laws,重构/组合会改变semantics。

名字帮助跨Option/Either/Task/IEnumerable识别pattern,domain code仍应使用业务词。不要在每个call site讲category theory;library implementer和reviewer必须理解laws,workflow reader看清data/failure flow即可。

The monad laws

Left identity:把value放入context再Bind f,等价f(value);right identity:m Bind pure等价m;associativity:(m Bind f) Bind g等价m Bind (x => f(x) Bind g)。Equality按context observable semantics,例如sequence order/error choice/effect count。

Prop.ForAll<Value>(value =>
    Pure(value).Bind(Parse).Equals(Parse(value)));
分步1 / 3

切换functor、applicative、monad与effect equality

Equality and effect evidence

Law test先定义“相等”再生成样本:Option比较Some/None和值,Either还比较chosen error,IEnumerable比较元素与顺序,Task则需同时观察completion、fault、cancellation及effect trace。对会访问时钟、网络或日志的function,用recording interpreter收集调用次数、参数与顺序,不能只比较最终value,否则重复执行effect的错误仍会伪装成law成立。Generator还应覆盖empty、boundary、Left/Right与throwing function,并在失败时保留可复现seed和最小反例。

Improving readability by using LINQ with any monad

C# query syntax通过Select/SelectMany/Where pattern可用于自定义Option/Either等context。From clauses为intermediate values命名,适合dependent chain;最终compiler仍调用methods,不自动处理mixed contexts或验证laws。

var result =
    from customer in FindCustomer(id)
    from order in FindLatestOrder(customer.Id)
    select BuildSummary(customer, order);

LINQ提高readability的前提是一个consistent context和abstraction level。若每行转换Task/Either/Option层,explicit named helpers可能更清楚。

When to use Bind vs. Apply

Independent computations用Apply,可parallel或accumulate;dependent computations用Bind,后一步需要前值且前失败就跳过。选择是data dependency,不是个人style。Task+Validation等mixed effects需Traverse/helper或分阶段;不要用嵌套Bind制造金字塔。

分步1 / 3

切换independent、dependent、LINQ与mixed effects

本章回顾:Context里的多参数组合由Dependency决定

  1. Map、Apply、Bind、Traverse对应不同plain/elevated shapes。
  2. Functor/applicative/monad是能力+laws,不是标签。
  3. Laws保护regrouping与refactor的observable equivalence。
  4. LINQ可表达monadic dependent chains,但不解决mixed effects。
  5. Independent用Apply,dependent用Bind,复杂layers显式staging。

练习

问题 1:Name和Email validation为什么适合Apply?

问题 2:Custom Option应测试哪些laws?

问题 3:Task of Either list如何组合?

术语表

名词解释

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

elevated value
applicative apply
context composition contract
LINQ monadic notation
dependency-shaped composition

原版目录概念补充核对

以下条目补齐官方目录中容易被示例主线掩盖的概念。它们不重复罗列目录,而是明确每项概念的机制、适用边界和验收证据。

Functors, applicatives, monads:机制、边界与证据

Chapter 8. Working effectively with multi-argument functions中的Functors, applicatives, monads应写成可组合的输入—输出契约,并把环境读取、状态改变与失败显式放在边界。用确定输入运行正常、空值和失败样本,同时记录返回值与副作用轨迹,证明结论不依赖隐藏状态。

The monad laws:机制、边界与证据

Chapter 8. Working effectively with multi-argument functions中的The monad laws应写成可组合的输入—输出契约,并把环境读取、状态改变与失败显式放在边界。用确定输入运行正常、空值和失败样本,同时记录返回值与副作用轨迹,证明结论不依赖隐藏状态。

When to use Bind vs. Apply:机制、边界与证据

Chapter 8. Working effectively with multi-argument functions中的When to use Bind vs. Apply应写成可组合的输入—输出契约,并把环境读取、状态改变与失败显式放在边界。用确定输入运行正常、空值和失败样本,同时记录返回值与副作用轨迹,证明结论不依赖隐藏状态。

讨论

评论区加载中…