函子与 Monad
函子(Functor)
定义: 实现了 map 方法的对象,可以在内部值上应用函数。
接口:
typescript
interface Functor<T> {
map<U>(fn: (value: T) => U): Functor<U>
}Maybe 函子
作用: 处理可能为空的值,避免空指针异常。
javascript
class Maybe {
constructor(value) {
this.value = value
}
static of(value) {
return new Maybe(value)
}
isNothing() {
return this.value === null || this.value === undefined
}
map(fn) {
return this.isNothing() ? Maybe.of(null) : Maybe.of(fn(this.value))
}
// 提取值
getOrElse(defaultValue) {
return this.isNothing() ? defaultValue : this.value
}
}
// 使用
const maybeName = Maybe.of('Alice')
maybeName.map(name => name.toUpperCase()) // Maybe('ALICE')
const maybeNull = Maybe.of(null)
maybeNull.map(x => x.toUpperCase()) // Maybe(null)
maybeNull.getOrElse('Unknown') // 'Unknown'
// 链式调用
Maybe.of(user)
.map(u => u.address)
.map(a => a.city)
.getOrElse('Unknown')Either 函子
作用: 处理错误,分为 Left(错误)和 Right(成功)。
javascript
class Either {
constructor(value) {
this.value = value
}
static left(value) {
return new Left(value)
}
static right(value) {
return new Right(value)
}
static of(value) {
return new Right(value)
}
}
class Left extends Either {
map(fn) {
return this // Left 不执行 map
}
getOrElse(defaultValue) {
return defaultValue
}
orElse(fn) {
return fn(this.value)
}
}
class Right extends Either {
map(fn) {
return Either.of(fn(this.value))
}
getOrElse(defaultValue) {
return this.value
}
orElse(fn) {
return this
}
}
// 使用
const parseJSON = (json) => {
try {
return Either.right(JSON.parse(json))
} catch (e) {
return Either.left(e.message)
}
}
parseJSON('{"name": "Alice"}')
.map(obj => obj.name)
.map(name => name.toUpperCase())
.getOrElse('error')
parseJSON('invalid')
.map(obj => obj.name) // 不执行
.getOrElse('Parse error') // 'Parse error'IO 函子
作用: 延迟执行副作用操作。
javascript
class IO {
constructor(fn) {
this.fn = fn
}
static of(value) {
return new IO(() => value)
}
static from(fn) {
return new IO(fn)
}
map(fn) {
return new IO(() => fn(this.fn()))
}
chain(fn) {
return new IO(() => fn(this.fn()).fn())
}
run() {
return this.fn()
}
}
// 使用
const readLocalStorage = key => IO.of(localStorage.getItem(key))
const log = message => new IO(() => {
console.log(message)
return message
})
const getUser = readLocalStorage('user')
.map(str => JSON.parse(str))
.map(user => user.name)
.chain(name => log(`Hello ${name}`))
getUser.run() // 执行所有 IO 操作Monad
定义: 实现了 flatMap(或 chain)方法的函子,用于嵌套处理。
接口:
typescript
interface Monad<T> extends Functor<T> {
flatMap<U>(fn: (value: T) => Monad<U>): Monad<U>
}Monad 定律
javascript
// 1. 左单位律
// Monad.of(a).flatMap(f) === f(a)
Either.of(5).flatMap(x => Either.of(x * 2)) // === Either.of(10)
// 2. 右单位律
// m.flatMap(Monad.of) === m
Either.of(5).flatMap(Either.of) // === Either.of(5)
// 3. 结合律
// m.flatMap(f).flatMap(g) === m.flatMap(x => f(x).flatMap(g))
const f = x => Either.of(x + 1)
const g = x => Either.of(x * 2)
Either.of(5).flatMap(f).flatMap(g)
// === Either.of(5).flatMap(x => f(x).flatMap(g))Promise 就是 Monad
javascript
// Promise 符合 Monad 接口
const promise = Promise.resolve(5)
promise.then(x => x * 2) // map
promise.then(x => Promise.resolve(x * 2)) // flatMap/chain常用函子组合
Either 与 Maybe 组合
javascript
class Maybe {
constructor(value) { this.value = value }
static of(value) { return new Maybe(value) }
static nothing() { return new Maybe(null) }
map(fn) {
return this.value === null || this.value === undefined
? Maybe.nothing()
: Maybe.of(fn(this.value))
}
flatMap(fn) {
return this.map(fn).value || Maybe.nothing()
}
}
const safeDiv = (a, b) =>
b === 0 ? Either.left('Division by zero') : Either.right(a / b)
const getUserEmail = user =>
Maybe.of(user)
.map(u => u.profile)
.map(p => p.email)
.valueTask 函子(异步)
javascript
// 简化的 Task 实现
class Task {
constructor(fork) {
this.fork = fork
}
static of(value) {
return new Task(resolve => resolve(value))
}
static rejected(reason) {
return new Task((_, reject) => reject(reason))
}
map(fn) {
return new Task((resolve, reject) =>
this.fork(
value => resolve(fn(value)),
reject
)
)
}
flatMap(fn) {
return new Task((resolve, reject) =>
this.fork(
value => fn(value).fork(resolve, reject),
reject
)
)
}
}
// 使用
const fetchUser = id => Task.of(
new Promise((resolve) => {
setTimeout(() => resolve({ id, name: 'Alice' }), 100)
})
)
fetchUser(1)
.map(user => user.name)
.flatMap(name => fetchUser(2).map(u => `${name} & ${u.name}`))
.fork(
console.log, // 成功
console.error // 失败
)实际应用:验证器
javascript
class Validator {
constructor(validations) {
this.validations = validations
}
static of(validations) {
return new Validator(validations)
}
static lift(validations) {
return value => Either.valid(validations.every(v => v.predicate(value)))
}
validate(value) {
const errors = this.validations
.filter(v => !v.predicate(value))
.map(v => v.message)
return errors.length === 0
? Either.right(value)
: Either.left(errors)
}
}
// 定义验证规则
const validateUser = Validator.of([
{ predicate: u => u.name, message: 'Name is required' },
{ predicate: u => u.name.length >= 2, message: 'Name too short' },
{ predicate: u => u.email, message: 'Email is required' },
{ predicate: u => u.email.includes('@'), message: 'Invalid email' }
])
validateUser({ name: 'A', email: 'invalid' })
.map(user => saveUser(user))
.leftMap(errors => console.log('Errors:', errors))面试高频题
Q1: Functor 和 Monad 的区别?
答案:
- Functor:只实现了
map,可以在值上应用函数 - Monad:实现了
flatMap/chain,可以处理嵌套的函子
javascript
// Functor:map 返回 Functor
Maybe.of(5).map(x => x * 2) // Maybe(10)
// Monad:flatMap 返回 Monad,且会扁平化
Maybe.of(Maybe.of(5)).flatMap(x => x) // Maybe(5)Q2: Maybe 和 Either 的适用场景?
答案:
- Maybe:表示值可能存在或不存在(null/undefined),不关心错误原因
- Either:表示两种互斥情况(成功/失败),可以携带错误信息
Q3: 什么是函子定律?
答案:
- 身份律:
functor.map(x => x) === functor - 组合律:
functor.map(f).map(g) === functor.map(x => g(f(x)))