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

    为什么Functional Programming先改变思…

    Imperative code常把“做什么、按什么顺序、改了谁”写在同一块;functional style尝试把behavior当value,把data transition写成从input到output的mapping,把不可避免的I/O留给明确boundary。重点不是禁止class、loop…

  2. 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. 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多次执行是否必然给相同结果。答案都是否。

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

切换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是否更明确。

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。

分步1 / 3

切换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降低可变状态与隐形依赖。

本章回顾:Behavior、Data与Effect各有位置

  1. Functional style把behavior当value、state change当transformation、effect留给owner boundary。
  2. C#提供delegates、lambdas、LINQ和generics,但purity与immutability依赖设计纪律。
  3. Function-as-map迫使signature暴露domain、codomain与hidden dependency。
  4. HOF可抽象policy和lifecycle,必须保留control flow与resource ownership。
  5. 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应写成可组合的输入—输出契约,并把环境读取、状态改变与失败显式放在边界。用确定输入运行正常、空值和失败样本,同时记录返回值与副作用轨迹,证明结论不依赖隐藏状态。

讨论

评论区加载中…