# 函数式编程

# 什么是函数式编程

  • 理解函数式编程
  • 与其他编程方式的区别,差异等

函数式编程(Functional Programming,FP)是一种编程范式,它将计算视为数学函数的求值过程,强调不可变数据纯函数的使用。

# 核心特征

  1. 纯函数(Pure Functions):相同的输入总是产生相同的输出,无副作用
  2. 不可变性(Immutability):数据一旦创建就不能修改
  3. 第一等公民函数:函数可作为参数、返回值和变量
  4. 声明式风格:关注"做什么"而非"怎么做"
  5. 递归替代循环:使用递归进行迭代

# 与命令式编程的对比

// 命令式编程(Imperative)
let sum = 0;
for (let i = 0; i < array.length; i++) {
    sum += array[i];
}

// 函数式编程(Functional)
const sum = array.reduce((acc, val) => acc + val, 0);

# 优势

  • ✅ 更容易测试和调试
  • ✅ 更好的可组合性
  • ✅ 线程安全(无共享状态)
  • ✅ 代码更简洁、可读性更高

# 闭包

闭包是指函数能够记住并访问其词法作用域,即使函数在其作用域外执行。

# 核心概念

function createCounter() {
    let count = 0; // 私有变量
    
    return function() {
        count++;
        return count;
    };
}

const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2

# 实际应用场景

# 1. 数据封装

function createBankAccount(initialBalance) {
    let balance = initialBalance;
    
    return {
        deposit: (amount) => {
            balance += amount;
            return balance;
        },
        withdraw: (amount) => {
            if (amount <= balance) {
                balance -= amount;
                return balance;
            }
            throw new Error('余额不足');
        },
        getBalance: () => balance
    };
}

# 2. 函数工厂

function multiplier(factor) {
    return function(number) {
        return number * factor;
    };
}

const double = multiplier(2);
const triple = multiplier(3);

console.log(double(5)); // 10
console.log(triple(5)); // 15

# 3. 回调和事件处理

function setupButton(buttonId, message) {
    const button = document.getElementById(buttonId);
    
    button.addEventListener('click', function() {
        alert(message); // 记住 message 参数
    });
}

# 注意事项

  • ⚠️ 闭包会占用内存,避免滥用
  • ⚠️ 在循环中创建闭包时注意变量捕获问题

# 纯函数

纯函数是函数式编程的基石,具有两个核心特性:

  1. 确定性:相同输入总是返回相同输出
  2. 无副作用:不修改外部状态或依赖外部可变状态

# 纯函数示例

// ✅ 纯函数
function add(a, b) {
    return a + b;
}

function square(x) {
    return x * x;
}

// ❌ 非纯函数
let counter = 0;
function increment() {
    counter++; // 有副作用
    return counter;
}

function getRandom() {
    return Math.random(); // 输出不确定
}

# 可缓存(Memoization)

纯函数可以被缓存以提高性能:

function memoize(fn) {
    const cache = new Map();
    
    return function(...args) {
        const key = JSON.stringify(args);
        
        if (cache.has(key)) {
            console.log('从缓存读取');
            return cache.get(key);
        }
        
        const result = fn(...args);
        cache.set(key, result);
        return result;
    };
}

// 使用示例
const expensiveCalculation = memoize(function(n) {
    console.log('计算中...');
    let result = 0;
    for (let i = 0; i < n; i++) {
        result += i;
    }
    return result;
});

expensiveCalculation(100); // 计算中...
expensiveCalculation(100); // 从缓存读取

# 可测试性

纯函数无需 mock 外部依赖,测试极其简单:

// 纯函数测试
function calculateTotal(price, quantity, tax) {
    return price * quantity * (1 + tax);
}

// 测试用例
calculateTotal(100, 2, 0.1); // 总是返回 220

# 柯里化

柯里化是将接受多个参数的函数转换为一系列只接受一个参数的函数的技术。

# 基础示例

// 普通函数
function add(a, b, c) {
    return a + b + c;
}

// 柯里化版本
function curriedAdd(a) {
    return function(b) {
        return function(c) {
            return a + b + c;
        };
    };
}

add(1, 2, 3);           // 6
curriedAdd(1)(2)(3);    // 6

# 通用柯里化工具

function curry(fn) {
    return function curried(...args) {
        if (args.length >= fn.length) {
            return fn.apply(this, args);
        } else {
            return function(...args2) {
                return curried.apply(this, args.concat(args2));
            };
        }
    };
}

// 使用
function multiply(a, b, c) {
    return a * b * c;
}

const curriedMultiply = curry(multiply);
multiply(2, 3, 4);              // 24
curriedMultiply(2)(3)(4);       // 24
curriedMultiply(2, 3)(4);       // 24
curriedMultiply(2)(3, 4);       // 24

# 实际应用:参数复用

// 日志函数
function logger(level, module, message) {
    console.log(`[${level}] [${module}]: ${message}`);
}

const curriedLogger = curry(logger);

// 创建特定模块的日志器
const appLogger = curriedLogger('INFO')('App');
appLogger('应用启动');  // [INFO] [App]: 应用启动
appLogger('用户登录');  // [INFO] [App]: 用户登录

# 组合

函数组合是将多个函数组合成一个新函数,从右到左依次执行。

# 基础组合函数

function compose(...fns) {
    return function(x) {
        return fns.reduceRight((acc, fn) => fn(acc), x);
    };
}

// 辅助函数
const add5 = x => x + 5;
const double = x => x * 2;
const square = x => x * x;

// 组合:square(double(add5(3)))
const transform = compose(square, double, add5);
transform(3); // ((3 + 5) * 2)² = 256

# pipe(从左到右)

function pipe(...fns) {
    return function(x) {
        return fns.reduce((acc, fn) => fn(acc), x);
    };
}

// pipe: add5 -> double -> square
const transformPipe = pipe(add5, double, square);
transformPipe(3); // 256

# 实际应用:数据处理管道

const users = [
    { name: 'Alice', age: 25, active: true },
    { name: 'Bob', age: 30, active: false },
    { name: 'Charlie', age: 35, active: true }
];

// 辅助函数
const filterActive = users => users.filter(u => u.active);
const getAges = users => users.map(u => u.age);
const sum = (arr) => arr.reduce((a, b) => a + b, 0);
const average = arr => arr.length ? sum(arr) / arr.length : 0;

// 组合:计算活跃用户的平均年龄
const calculateActiveAvgAge = pipe(
    filterActive,
    getAges,
    average
);

calculateActiveAvgAge(users); // 30

# pointfree

Pointfree(无点风格)是一种函数式编程风格,函数定义中不提及操作的参数。

# 对比示例

// 非 pointfree
function doubleAndAdd5(numbers) {
    return numbers.map(n => n * 2).map(n => n + 5);
}

// pointfree 风格
const double = n => n * 2;
const add5 = n => n + 5;
const doubleAndAdd5PF = numbers => numbers.map(double).map(add5);

// 更纯粹的 pointfree(需要工具库支持)
const map = fn => array => array.map(fn);
const doubleAndAdd5Pointfree = pipe(
    map(double),
    map(add5)
);

# 优势

  • ✅ 更抽象、更通用
  • ✅ 强调函数组合而非数据操作
  • ✅ 代码更简洁

# 注意事项

⚠️ 过度追求 pointfree 可能降低可读性,需平衡

# 函子

函子(Functor)是一个包含值的容器类型,支持 map 操作来转换内部值。

# Context

Context(上下文)是函数式编程中的一个抽象概念,表示计算发生的环境或背景。

// 不同的 Context 示例

// 1. Array Context - 表示多个值
[1, 2, 3].map(x => x * 2); // [2, 4, 6]

// 2. Promise Context - 表示异步值
Promise.resolve(5).then(x => x * 2); // Promise(10)

// 3. Maybe Context - 表示可能不存在的值
Maybe.of(5).map(x => x * 2); // Maybe(10)

// 4. IO Context - 表示有副作用的计算
IO.of(5).map(x => x * 2); // IO(10)

# Pointed Container

Pointed Container 是带有 of(或 pure)方法的容器,可以将任意值放入容器中。

class Pointed {
    constructor(value) {
        this.value = value;
    }
    
    static of(value) {
        return new Pointed(value);
    }
    
    map(fn) {
        return Pointed.of(fn(this.value));
    }
}

// 使用示例
const result = Pointed.of(5)
    .map(x => x + 1)
    .map(x => x * 2);

console.log(result.value); // 12

常见 Pointed Functor:

  • Maybe.of(value)
  • Either.of(value) (返回 Right)
  • IO.of(value)
  • Task.of(value)

# Functor

Functor 定律确保 map 行为的一致性:

// Functor 定律

// 1. 恒等定律:map(id) === id
const identity = x => x;
Maybe.of(5).map(identity); // Maybe(5)

// 2. 复合定律:map(compose(f, g)) === map(g).map(f)
const f = x => x + 1;
const g = x => x * 2;

// 左边
Maybe.of(5).map(x => f(g(x))); // Maybe(11)

// 右边
Maybe.of(5).map(g).map(f); // Maybe(11)

# ap

ap 方法用于应用一个包含函数的容器到另一个容器。

class Maybe {
    constructor(value) {
        this.value = value;
    }
    
    static of(value) {
        return new Maybe(value);
    }
    
    map(fn) {
        if (this.value === null || this.value === undefined) {
            return this;
        }
        return Maybe.of(fn(this.value));
    }
    
    ap(other) {
        if (this.value === null || this.value === undefined) {
            return this;
        }
        if (other.value === null || other.value === undefined) {
            return other;
        }
        return Maybe.of(this.value(other.value));
    }
}

// 使用示例
const add = x => y => x + y;

const maybeAdd = Maybe.of(add(5));
const maybeNum = Maybe.of(3);

maybeAdd.ap(maybeNum); // Maybe(8)

// 处理失败情况
const nothing = Maybe.of(null);
maybeAdd.ap(nothing); // Maybe(null)

多参数函数的应用:

const add3 = (x, y, z) => x + y + z;

// 使用 ap 链接多个参数
const result = Maybe.of(add3)
    .map(curry(add3))
    .ap(Maybe.of(1))
    .ap(Maybe.of(2))
    .ap(Maybe.of(3));

result.value; // 6

# Monad

Monad 是支持链式操作的容器,提供 chain(或 flatMap)方法来避免嵌套。

class Maybe {
    constructor(value) {
        this.value = value;
    }
    
    static of(value) {
        return new Maybe(value);
    }
    
    map(fn) {
        if (this.value === null || this.value === undefined) {
            return this;
        }
        return Maybe.of(fn(this.value));
    }
    
    chain(fn) {
        if (this.value === null || this.value === undefined) {
            return this;
        }
        return fn(this.value);
    }
    
    // join 用于扁平化嵌套的 Monad
    join() {
        return this.value instanceof Maybe ? this.value : this;
    }
}

// 问题场景:返回嵌套 Monad
function findUser(id) {
    return Maybe.of({ id, name: 'Alice' });
}

function getUserEmail(user) {
    return Maybe.of(user.email);
}

// ❌ 使用 map 会产生嵌套
Maybe.of(1)
    .map(findUser)      // Maybe(Maybe(User))
    .map(getUserEmail); // Maybe(Maybe(Maybe(Email)))

// ✅ 使用 chain 扁平化
Maybe.of(1)
    .chain(findUser)     // Maybe(User)
    .chain(getUserEmail); // Maybe(Email)

Monad 定律:

// 1. 左单位元:of(x).chain(f) === f(x)
Maybe.of(5).chain(x => Maybe.of(x + 1)); // Maybe(6)

// 2. 右单位元:m.chain(of) === m
Maybe.of(5).chain(Maybe.of); // Maybe(5)

// 3. 结合律:m.chain(f).chain(g) === m.chain(x => f(x).chain(g))
const f = x => Maybe.of(x + 1);
const g = x => Maybe.of(x * 2);

Maybe.of(5)
    .chain(f).chain(g); // Maybe(12)

Maybe.of(5)
    .chain(x => f(x).chain(g)); // Maybe(12)

# Task

Task 函子用于处理异步操作,是 Promise 的函子版本。

class Task {
    constructor(fork) {
        this.fork = fork; // fork 接收 (reject, resolve) 两个回调
    }
    
    static of(value) {
        return new Task((rej, res) => res(value));
    }
    
    static reject(err) {
        return new Task((rej, res) => rej(err));
    }
    
    map(fn) {
        return new Task((rej, res) => 
            this.fork(
                rej,
                val => res(fn(val))
            )
        );
    }
    
    chain(fn) {
        return new Task((rej, res) => 
            this.fork(
                rej,
                val => fn(val).fork(rej, res)
            )
        );
    }
    
    // 转换为 Promise
    promise() {
        return new Promise((resolve, reject) => 
            this.fork(reject, resolve)
        );
    }
    
    // 执行任务
    run(onError, onSuccess) {
        this.fork(onError, onSuccess);
    }
}

// 使用示例
const readFile = filename => new Task((rej, res) => {
    // 模拟异步读取
    setTimeout(() => {
        res(`Content of ${filename}`);
    }, 100);
});

const parseJSON = str => new Task((rej, res) => {
    try {
        res(JSON.parse(str));
    } catch (e) {
        rej(e);
    }
});

// 链式异步操作
readFile('data.json')
    .chain(parseJSON)
    .map(data => data.user)
    .run(
        err => console.error('错误:', err),
        user => console.log('用户:', user)
    );

并发任务处理:

class Task {
    // ... 上述方法 ...
    
    static all(tasks) {
        return new Task((rej, res) => {
            const results = [];
            let completed = 0;
            
            tasks.forEach((task, index) => {
                task.run(
                    rej,
                    val => {
                        results[index] = val;
                        completed++;
                        if (completed === tasks.length) {
                            res(results);
                        }
                    }
                );
            });
        });
    }
}

// 并发请求多个 API
const fetchUser = id => new Task((rej, res) => {
    fetch(`/api/users/${id}`)
        .then(r => r.json())
        .then(res)
        .catch(rej);
});

Task.all([
    fetchUser(1),
    fetchUser(2),
    fetchUser(3)
])
.map(users => users.map(u => u.name))
.run(console.error, console.log);

# Reader 函子(依赖注入)

class Reader {
    constructor(run) {
        this.run = run; // run(env) => value
    }
    
    static of(value) {
        return new Reader(() => value);
    }
    
    map(fn) {
        return new Reader(env => fn(this.run(env)));
    }
    
    chain(fn) {
        return new Reader(env => fn(this.run(env)).run(env));
    }
}

// 使用示例
const getConfig = key => new Reader(config => config[key]);
const getUser = userId => new Reader(config => 
    ({ id: userId, name: config.userName })
);

// 组合依赖
const app = getConfig('apiUrl')
    .chain(url => getUser(1)
        .map(user => ({ url, user }))
    );

app.run({ apiUrl: '/api', userName: 'Alice' });
// { url: '/api', user: { id: 1, name: 'Alice' } }

# State 函子(状态管理)

class State {
    constructor(run) {
        this.run = run; // run(state) => [value, newState]
    }
    
    static of(value) {
        return new State(state => [value, state]);
    }
    
    map(fn) {
        return new State(state => {
            const [value, newState] = this.run(state);
            return [fn(value), newState];
        });
    }
    
    chain(fn) {
        return new State(state => {
            const [value, currentState] = this.run(state);
            return fn(value).run(currentState);
        });
    }
}

// 辅助函数
const get = new State(state => [state, state]);
const set = state => new State(() => [undefined, state]);
const modify = fn => new State(state => [undefined, fn(state)]);

// 使用示例:计数器
const increment = modify(count => count + 1);
const double = modify(count => count * 2);

const program = increment
    .chain(() => double)
    .chain(() => get);

program.run(5); // [12, 12] (值,状态)

# List 函子(多值计算)

class List {
    constructor(values) {
        this.values = values;
    }
    
    static of(...values) {
        return new List(values);
    }
    
    map(fn) {
        return new List(this.values.map(fn));
    }
    
    chain(fn) {
        return new List(
            this.values
                .map(v => fn(v).values)
                .flat()
        );
    }
    
    filter(predicate) {
        return new List(this.values.filter(predicate));
    }
}

// 使用示例:笛卡尔积
const pairs = List.of(1, 2, 3)
    .chain(x => 
        List.of('a', 'b', 'c')
            .map(y => [x, y])
    );

pairs.values; 
// [[1,'a'], [1,'b'], [1,'c'], [2,'a'], [2,'b'], [2,'c'], [3,'a'], [3,'b'], [3,'c']]

// 实际应用:搜索
const search = query => 
    List.of(query)
        .map(q => q.toLowerCase())
        .filter(q => q.length > 2);

search('App').values; // ['app']
search('Ap').values;  // []

# Context

# Pointed Container

# Functor

# ap

# Monad

# task

# task

(已在上面 Task 部分详细说明)

# Maybe 函子(处理 null/undefined)

class Maybe {
    constructor(value) {
        this.value = value;
    }
    
    static of(value) {
        return new Maybe(value);
    }
    
    map(fn) {
        if (this.value === null || this.value === undefined) {
            return this;
        }
        return Maybe.of(fn(this.value));
    }
    
    chain(fn) {
        if (this.value === null || this.value === undefined) {
            return this;
        }
        return fn(this.value);
    }
    
    getOrElse(defaultValue) {
        return (this.value === null || this.value === undefined) 
            ? defaultValue 
            : this.value;
    }
    
    // 过滤
    filter(predicate) {
        if (this.value === null || this.value === undefined) {
            return this;
        }
        return predicate(this.value) ? this : Maybe.of(null);
    }
    
    // 判断是否有值
    isNothing() {
        return this.value === null || this.value === undefined;
    }
}

// 使用示例
const user = { name: 'Alice', email: 'alice@example.com' };

// 安全访问嵌套属性
const getEmail = user => 
    Maybe.of(user)
        .map(u => u.email)
        .getOrElse('no-email@example.com');

getEmail(user); // 'alice@example.com'
getEmail(null); // 'no-email@example.com'

# Either 函子(错误处理)

class Either {
    constructor(value) {
        this.value = value;
    }
    
    static of(value) {
        return new Right(value);
    }
    
    static left(value) {
        return new Left(value);
    }
}

class Left extends Either {
    map() { return this; }
    chain() { return this; }
    getOrElse(defaultValue) { return defaultValue; }
    
    toString() {
        return `Left(${this.value})`;
    }
}

class Right extends Either {
    map(fn) { return new Right(fn(this.value)); }
    chain(fn) { return fn(this.value); }
    getOrElse() { return this.value; }
    
    toString() {
        return `Right(${this.value})`;
    }
}

function tryCatch(fn, errorHandler = identity) {
    try {
        return new Right(fn());
    } catch (e) {
        return new Left(errorHandler(e));
    }
}

// 使用示例
const result = tryCatch(() => JSON.parse('{"valid": true}'))
    .map(data => data.valid)
    .getOrElse(false);

// 链式错误处理
const safeDivide = (x, y) => 
    y === 0 
        ? Either.left(new Error('除数不能为零'))
        : Either.of(x / y);

safeDivide(10, 2)
    .map(x => x * 2)
    .getOrElse(0); // 10

safeDivide(10, 0)
    .map(x => x * 2)
    .getOrElse(0); // 0

# IO 函子(处理副作用)

class IO {
    constructor(effect) {
        this.effect = effect; // 一个函数
    }
    
    static of(value) {
        return new IO(() => value);
    }
    
    map(fn) {
        return new IO(() => fn(this.effect()));
    }
    
    chain(fn) {
        return new IO(() => fn(this.effect()).effect());
    }
    
    run() {
        return this.effect();
    }
}

// 延迟执行副作用
const io = new IO(() => Math.random())
    .map(n => n * 100)
    .map(n => Math.floor(n));

io.run(); // 执行时才产生随机数

// 实际应用:日志
const log = msg => new IO(() => console.log(msg));
const getTime = () => new IO(() => new Date().toISOString());

log('开始')
    .chain(() => 
        getTime()
            .map(time => `当前时间:${time}`)
            .chain(log)
    )
    .chain(() => log('结束'))
    .run();

# Reader 函子(依赖注入)

class Reader {
    constructor(run) {
        this.run = run; // run(env) => value
    }
    
    static of(value) {
        return new Reader(() => value);
    }
    
    map(fn) {
        return new Reader(env => fn(this.run(env)));
    }
    
    chain(fn) {
        return new Reader(env => fn(this.run(env)).run(env));
    }
}

// 使用示例
const getConfig = key => new Reader(config => config[key]);
const getUser = userId => new Reader(config => 
    ({ id: userId, name: config.userName })
);

// 组合依赖
const app = getConfig('apiUrl')
    .chain(url => getUser(1)
        .map(user => ({ url, user }))
    );

app.run({ apiUrl: '/api', userName: 'Alice' });
// { url: '/api', user: { id: 1, name: 'Alice' } }

# State 函子(状态管理)

class State {
    constructor(run) {
        this.run = run; // run(state) => [value, newState]
    }
    
    static of(value) {
        return new State(state => [value, state]);
    }
    
    map(fn) {
        return new State(state => {
            const [value, newState] = this.run(state);
            return [fn(value), newState];
        });
    }
    
    chain(fn) {
        return new State(state => {
            const [value, currentState] = this.run(state);
            return fn(value).run(currentState);
        });
    }
}

// 辅助函数
const get = new State(state => [state, state]);
const set = state => new State(() => [undefined, state]);
const modify = fn => new State(state => [undefined, fn(state)]);

// 使用示例:计数器
const increment = modify(count => count + 1);
const double = modify(count => count * 2);

const program = increment
    .chain(() => double)
    .chain(() => get);

program.run(5); // [12, 12] (值,状态)

# List 函子(多值计算)

class List {
    constructor(values) {
        this.values = values;
    }
    
    static of(...values) {
        return new List(values);
    }
    
    map(fn) {
        return new List(this.values.map(fn));
    }
    
    chain(fn) {
        return new List(
            this.values
                .map(v => fn(v).values)
                .flat()
        );
    }
    
    filter(predicate) {
        return new List(this.values.filter(predicate));
    }
}

// 使用示例:笛卡尔积
const pairs = List.of(1, 2, 3)
    .chain(x => 
        List.of('a', 'b', 'c')
            .map(y => [x, y])
    );

pairs.values; 
// [[1,'a'], [1,'b'], [1,'c'], [2,'a'], [2,'b'], [2,'c'], [3,'a'], [3,'b'], [3,'c']]

// 实际应用:搜索
const search = query => 
    List.of(query)
        .map(q => q.toLowerCase())
        .filter(q => q.length > 2);

search('App').values; // ['app']
search('Ap').values;  // []

# 实际应用

# 1. React 中的函数式编程

// 纯组件
const UserCard = ({ user }) => (
    <div className="card">
        <h2>{user.name}</h2>
        <p>{user.email}</p>
    </div>
);

// 函数组合
const EnhancedUserCard = pipe(
    withAuthentication,
    withLoading,
    UserCard
);

# 2. 数据处理管道

// ETL 流程
const processSalesData = pipe(
    filterValidRecords,
    groupByRegion,
    calculateTotals,
    sortByRevenue,
    formatOutput
);

const result = processSalesData(rawData);

# 3. 函数式 HTTP 请求

// 使用 fetch + 函子风格
const apiCall = url => 
    fetch(url)
        .then(response => Maybe.of(response))
        .map(r => r.ok ? Right.of(r) : Left.of(r.status))
        .getOrElse(Left.of('Network error'));

# 4. Redux 中的函数式思想

// Reducer 必须是纯函数
function counterReducer(state = 0, action) {
    switch (action.type) {
        case 'INCREMENT':
            return state + 1; // 不修改原状态
        case 'DECREMENT':
            return state - 1;
        default:
            return state;
    }
}

# 5. 函数式工具库推荐

  • Ramda: 完整的函数式工具库
  • Lodash/fp: Lodash 的函数式版本
  • Folktale: 提供 Maybe、Either 等数据类型
  • Immutable.js: 不可变数据结构

# 最佳实践总结

  1. 🎯 优先使用纯函数
  2. 🔄 多用高阶函数(map、filter、reduce)
  3. 📦 使用函子处理边界情况
  4. 🔗 通过组合构建复杂逻辑
  5. ⚡ 适度柯里化提高复用性
  6. 🚫 避免可变状态和副作用
本章目录