Chapter 1. Introducing functional programming
覆盖first-class functions、避免mutation、strong guarantees、C#函数式能力、function representation、HOF去重与收益,以显式behavior和effect boundary入门。
学习目标
- 能解释first-class functions、immutability与strong guarantees如何改变dependency、state transition和reasoning boundary
- 能比较method group、delegate、lambda、adapter与function factory的binding/capture语义,复现同一behavior的不同表示
- 能设计setup/execute/teardown型higher-order function,区分消除duplication与隐藏control flow的边界
机制总览
Chapter 1. Introducing functional programming:机制路径
- 1
为什么Functional Programming先改变思…
Imperative code常把“做什么、按什么顺序、改了谁”写在同一块;functional style尝试把behavior当value,把data transition写成从input到output的mapping,把不可避免的I/O留给明确boundary。重点不是禁止class、loop…
- 2
What is this thing called fun…
本书用三条可操作特征切入:functions as first-class values、avoiding state mutation、writing programs with strong guarantees。First-class让依赖behavior不必硬编码;immutable tra…
- 3
How functional a language is …
C 是multi-paradigm language。Delegates/lambdas把functions变成values,LINQ提供Map/Filter/Bind-like composition,generics表达container operations,C 6/7又加入expressio…
章级决策实验
Chapter 1. Introducing functional programming:机制与证据
切换《Chapter 1. Introducing functional programming》的三个关键教学阶段,先解释机制,再用运行与失败证据验证结论。
选择推理阶段
当前阶段 · 为什么Functional Programming先改变思…
Imperative code常把“做什么、按什么顺序、改了谁”写在同一块;functional style尝试把behavior当value,把data transition写成从input到output的mapping,把不可避免的I/O留给明确boundary。重点不是禁止class、loop…
可核验证据
以确定输入重复运行「为什么Functional Programming先改变思…」的最小管线,用属性测试、状态快照和副作用调用轨迹核对返回值、失败传播与资源边界。
学完《Chapter 1. Introducing functional programming》后,应能从输入和前置条件推导状态变化,并用可重复的构建、运行或边界测试证明结果。
失效—证据矩阵
Chapter 1. Introducing functional programming:失效与核验
为什么Functional Programming先改变思…
典型失效
若把「为什么Functional Programming先改变思…」只写成函数式术语而不隔离副作用、状态和失败分支,组合后的程序仍会依赖隐藏时序,无法从输入稳定推导结果。
核验证据
以确定输入重复运行「为什么Functional Programming先改变思…」的最小管线,用属性测试、状态快照和副作用调用轨迹核对返回值、失败传播与资源边界。
What is this thing called fun…
典型失效
若把「What is this thing called fun…」只写成函数式术语而不隔离副作用、状态和失败分支,组合后的程序仍会依赖隐藏时序,无法从输入稳定推导结果。
核验证据
以确定输入重复运行「What is this thing called fun…」的最小管线,用属性测试、状态快照和副作用调用轨迹核对返回值、失败传播与资源边界。
How functional a language is …
典型失效
若把「How functional a language is …」只写成函数式术语而不隔离副作用、状态和失败分支,组合后的程序仍会依赖隐藏时序,无法从输入稳定推导结果。
核验证据
以确定输入重复运行「How functional a language is …」的最小管线,用属性测试、状态快照和副作用调用轨迹核对返回值、失败传播与资源边界。
为什么Functional Programming先改变思考单位
Imperative code常把“做什么、按什么顺序、改了谁”写在同一块;functional style尝试把behavior当value,把data transition写成从input到output的mapping,把不可避免的I/O留给明确boundary。重点不是禁止class、loop或mutation,而是让reasoning所需信息尽量出现在signature和value flow里。
先预测:使用lambda是否就等于functional;LINQ query不修改source是否就保证整个program pure;把setup/teardown包进HOF是否永远更清楚;同一个capturing lambda多次执行是否必然给相同结果。答案都是否。
↡Function可以像其他value一样被赋值、作为argument传入、作为result返回并组成更大behavior。What is this thing called functional programming?
本书用三条可操作特征切入:functions as first-class values、avoiding state mutation、writing programs with strong guarantees。First-class让依赖behavior不必硬编码;immutable transformations让旧state仍可观察;strong guarantee让caller能依据explicit input推理result与failure,而不必先扫描global variables。
Mutation不是语法罪名,而是alias和ordering成本。List<T>.Sort()原地改变list,所有持有alias的code都观察到变化;OrderBy返回新的sequence view,source保持不变。后者仍可能deferred、capture或访问I/O,因此“non-mutating API”不自动等于pure。
var source = new[] { 3, 1, 2 };
var ordered = source.OrderBy(value => value).ToArray();
// source remains [3, 1, 2]; ordered is [1, 2, 3]切换first-class、mutation、immutable与guarantee
How functional a language is C#?
C#是multi-paradigm language。Delegates/lambdas把functions变成values,LINQ提供Map/Filter/Bind-like composition,generics表达container operations,C# 6/7又加入expression-bodied members、tuples、pattern等更适合value flow的syntax。但C#不会强制purity,也没有built-in algebraic data types或自动effect tracking。
因此functionality来自discipline和library design:immutable inputs、honest signatures、pure core、explicit effect shell。Modern C# records、nullable references与new patterns属于第二版及之后语境;本第一版正文保持C# 6/7边界,后续只作为versioned alternatives。
Thinking in functions
把function看作数学mapping并不要求所有business code都能写成公式,而是要求问:domain是什么、codomain是什么、哪些inputs未写出来、哪些outputs通过mutation或exception偷偷传递。C# method只有在转换到delegate/expression type后才作为value参与composition。
Func<Order, decimal> total = order => order.Lines.Sum(line => line.Price);
Func<decimal, string> format = value => value.ToString("C", culture);
Func<Order, string> display = order => format(total(order));切换method、lambda、adapter与factory
Higher-order functions
Higher-order function接收function、返回function,或两者兼有。Where依赖predicate,adapter把一个signature转换为另一个,factory通过capture生成specialized behavior。它让variation成为explicit argument,减少inheritance/config flags带来的隐形branch。
HOF也可能过度抽象:若传入functions数量太多、generic types难以读、control flow跨多层,reader只能在debugger里恢复执行顺序。判断标准是调用点是否更像domain sentence,effect/lifetime是否更明确。
↡接收或产生function value,从而抽象behavior variation、policy或lifecycle的function。Using HOFs to avoid duplication
典型重复不是业务action,而是数据库connection的open/close、transaction commit/rollback或lock acquire/release。HOF可以拥有setup和teardown,把差异action作为function传入。它必须通过using/try-finally保证normal与fault cleanup,并声明resource由谁拥有。
static T WithConnection<T>(Func<IDbConnection, T> use)
{
using var connection = OpenConnection();
connection.Open();
return use(connection);
}
var customer = WithConnection(db => LoadCustomer(db, id));把using变HOF的收益是lifecycle policy集中;代价是stack trace、async support、transaction boundary和multiple operations可能变复杂。Modern async resource需要separate async HOF/await using,不能让sync wrapper阻塞Task。
切换setup、execute、teardown与tradeoff
Benefits of functional programming
收益来自上述约束的组合:pure function容易test与parallelize;immutable value减少alias reasoning;composition让workflow按types连接;HOF集中policy;explicit outcomes减少exception/null surprise。它不是用更学术的词替换OOP,而是在适合的boundary降低可变状态与隐形依赖。
↡给定相同显式inputs时,可用value substitution替换function call而不改变program observable behavior的性质。本章回顾:Behavior、Data与Effect各有位置
- Functional style把behavior当value、state change当transformation、effect留给owner boundary。
- C#提供delegates、lambdas、LINQ和generics,但purity与immutability依赖设计纪律。
- Function-as-map迫使signature暴露domain、codomain与hidden dependency。
- HOF可抽象policy和lifecycle,必须保留control flow与resource ownership。
- Purity、immutability和composition共同带来testability、concurrency与maintenance收益。
练习
问题 1:一个lambda读取DateTime.Now但不写任何state,它pure吗?
问题 2:怎样判断resource HOF是否比重复using更好?
问题 3:First-class function最直接改善哪类dependency?
术语表
名词解释
本章出现的专业名词,用大白话再讲一遍。
- first-class behavior
- immutable transformation
- function-as-map model
- higher-order function
- substitution guarantee
原版目录概念补充核对
以下条目补齐官方目录中容易被示例主线掩盖的概念。它们不重复罗列目录,而是明确每项概念的机制、适用边界和验收证据。
How functional a language is C#?:机制、边界与证据
Chapter 1. Introducing functional programming中的How functional a language is C#?应写成可组合的输入—输出契约,并把环境读取、状态改变与失败显式放在边界。用确定输入运行正常、空值和失败样本,同时记录返回值与副作用轨迹,证明结论不依赖隐藏状态。
Thinking in functions:机制、边界与证据
Chapter 1. Introducing functional programming中的Thinking in functions应写成可组合的输入—输出契约,并把环境读取、状态改变与失败显式放在边界。用确定输入运行正常、空值和失败样本,同时记录返回值与副作用轨迹,证明结论不依赖隐藏状态。
Higher-order functions:机制、边界与证据
Chapter 1. Introducing functional programming中的Higher-order functions应写成可组合的输入—输出契约,并把环境读取、状态改变与失败显式放在边界。用确定输入运行正常、空值和失败样本,同时记录返回值与副作用轨迹,证明结论不依赖隐藏状态。
Using HOFs to avoid duplication:机制、边界与证据
Chapter 1. Introducing functional programming中的Using HOFs to avoid duplication应写成可组合的输入—输出契约,并把环境读取、状态改变与失败显式放在边界。用确定输入运行正常、空值和失败样本,同时记录返回值与副作用轨迹,证明结论不依赖隐藏状态。
Benefits of functional programming:机制、边界与证据
Chapter 1. Introducing functional programming中的Benefits of functional programming应写成可组合的输入—输出契约,并把环境读取、状态改变与失败显式放在边界。用确定输入运行正常、空值和失败样本,同时记录返回值与副作用轨迹,证明结论不依赖隐藏状态。