简洁有力,比什么“自函子范畴上的一个幺半群”的说法实用多了
https://www.reddit.com/r/ruby/comments/448b62/teaching_monads_to_rubyists_without_analogies/
其实就是 Array 这种纯容器不能延时求值,然后要给这个容器加个函数作为接口:
访问 → f []
之后对这个数组的操作都叠在 f 上,不 touch []
,
等到求值的时候再把 f 应用到[]
个人理解
可以看看 The category design pattern http://www.haskellforall.com/2012/08/the-category-design-pattern.html
functor 是 category 之间的函数 自函子 endofunctor 意思是定义域和值域都是同一个 category, 基本等于废话可以不管。
有单位元,运算符又满足结合律,就是 幺半群 monoid 的定义.
monad 中,单位元是 return
, 满足结合律的运算符是 bind (>>=
)
换句话说,monad 是定义在函数上的代数结构,它有单位元,又有满足结合律的运算符。
如果在 Ruby 数组上找 monad, 那么以 []
为单位元,以 .flat_map
为满足结合律的运算符,就构成一个 Array Monad.
例如把这段 Haskell 代码贴进 ghci
let a = 2
let b = [3, 4]
let c = ['a', 'b']
:{
do
x <- return a
y <- b
z <- c
return (x, y, z)
:}
执行结果是 [(2,3,'a'),(2,3,'b'),(2,4,'a'),(2,4,'b')]
. 注意 do notation 其实是一个让你省略括号和缩进的语法糖,再把 return
换成 []
, 解除语法糖后是这样的意思:
[a] >>= \x ->
b >>= \y ->
c >>= \z ->
[(x, y, z)]
那么对应 Ruby 代码是这样:
a = 2
b = [3, 4]
c = ['a', 'b']
[a].flat_map do |x|
b.flat_map do |y|
c.flat_map do |z|
[[x, y, z]]
end
end
end
执行结果是 [[2, 3, "a"], [2, 3, "b"], [2, 4, "a"], [2, 4, "b"]]
如果在 Ruby 对象上找 monad, 那么以 .itself
为单位元,以 .try
为满足结合律的运算符,那就构成一个 Maybe Monad
具体例子留作练习哈
有各种各样找 monad 的方式,但只要记住 "自函子上的幺半群", 没有一个能逃出你的眼睛...
我描述的是 functor 吧,monad 也是从类似的想法衍生出来的吧,就是先串函数,不碰 Just https://blog.oyanglul.us/javascript/functional-javascript.html 相关的东西是在这看的,但不知对不对,可以批判一下。。