15 分钟了解 Monad(上)

简介: 15 分钟了解 Monad

640.jpg看到函数式编程相关的资料的时候, 总是看到 Monad 这个词, 一直想了解一下, 然而查资料对 Monad 的定义往往是上来一大堆数学概念:

Monad 是一个自函子范畴上的幺半群

鉴于本人数学基础实在太差, 一直没能理解. 其实撇开这些数学概念来说, Monad 本身是一个非常简 单的东西, 像是 Rust 中的 Option 一样, 一旦理解, 就发现再也回不去之前没有他的世界了. Monad 并不仅局限于函数式编程语言, 也可以用其他的语言来表示.

例子

1 日志

假设我们有三个只接受一个参数的函数, f1, f2, f3, 分别返回 +1, +2, +3 后的数局以及一 条关于做了什么操作的信息.

def f1(x):
    return (x + 1, str(x) + "+1")
def f2(x):
    return (x + 2, str(x) + "+2")
def f3(x):
    return (x + 3, str(x) + "+3")

现在我们想要计算 x + 1 + 2 + 3, 那么我们可以把这三个函数链式调用. 而且, 我们还想获得关于 调用了那些函数的详细日志.

可以这样做:

log = "Ops:"
res, log1 = f1(x)
log += log1 + ";"
res, log2 = f2(res)
log += log2 + ";"
res, log3 = f3(res)
log += log3 + ";"
print(res, log)

这种方法简直太丑陋了, 首先我们重复编写了好多胶水代码, 而且如果我们要再添加一个函数 f4 的 话, 就得再多些两行胶水代码. 更糟糕的是, 不断改变 res 和 log 两个变量的值让我们的代码变得 非常不可读.

理想情况下, 我们希望能够这样链式调用: f3(f2(f1(x))). 不幸的是, f1 和 f2 的返回结果和 f2 和 f3 的入口参数是不一样的. 为了解决这个问题, 我们引入两个新的函数:

def unit(x):
    return(x, "Ops:")
def bind(t, f):
    res = f(t[0])
    return (res[0], t[1] + res[1] + ";")

这样的话, 我们就可以用下面的链式调用来解决了:

print(bind(bind(bind(unit(x), f1), f2), f3))

下面的图展示了当 x=0 时候的调用过程, v1, v2, v3 分别表示中间数据.640.pngunit 函数把参数 x 变成了 (int, str) 构成的 tuple. 接下来的 bind 函数调用了他的参数 f 函 数, 同时把结果累加到了形参 t 上.

这种方法避免了第一种方法的缺点, 因为所有的胶水代码都在 bind 函数中, 当我们要添加一个新的 函数的时候, 只需要接着链式调用就可以了.

print(bind(bind(bind(bind(unit(x), f1), f2), f3), f4))

2 中间值的列表

在这个例子中, 我们假设有三个简单的单参函数:

def f1(x): return x + 1
def f2(x): return x + 2
def f3(x): return x + 3

和前面的例子一样, 我们想要组合这些函数来计算 x+1+2+3 的值. 除此之外, 我们还想要生成中间 值得列表, 也就是: x, x+1, x+1+2, x+1+2+3.

和前面的例子不同的是, 这三个函数的输入和输出类型是匹配的, 因此我们可以直接调用 f3(f2(f1(x)). 不过这样做的话, 我们没法获得中间值.

一个可行的方法是:

lst = [x]
res = f1(x)
lst.append(res)
res = f2(res)
lst.append(res)
res = f3(res)
lst.append(res)
print(res, lst)

很显然, 这并不是一个很好的做法, 我们又写了一堆的胶水代码, 而且还得负责把中间变量聚合成一 个列表. 如果我们再添加一个新的函数 f4 的话, 又得再添加一些新的胶水代码了.

目录
相关文章
|
4月前
|
存储 JavaScript 前端开发
JavaScript——函数式编程Functor(函子)
JavaScript——函数式编程Functor(函子)
27 0
|
设计模式 存储 数据库
15 分钟了解 Monad(下)
15 分钟了解 Monad
90 0
|
JavaScript 容器
【函数式编程】基于JS进行函数式编程(四)函子 | MayBe函子 | Monad函子
【函数式编程】基于JS进行函数式编程(四)函子 | MayBe函子 | Monad函子
177 0
【函数式编程】基于JS进行函数式编程(四)函子 | MayBe函子 | Monad函子
|
JavaScript 前端开发 容器
promise is a monad?
态射是范畴内对象之间的映射关系。函子与它类似,函子是范畴与范畴间的映射关系,也就是可以通过一个函子,把一个范畴映射到另一个范畴。
1724 0
|
前端开发 JavaScript 程序员
Promise是Monad吗?
译者按: 近年来,函数式语言的特性都被其它语言学过去了。 原文: Functional Computational Thinking — What is a monad? 译者: Fundebug 为了保证可读性,本文采用意译而非直译。
845 0