Monad学习

这是观看Cousera上的课程《Principles of Reactive Programming》中week1里的Monad一节所做的笔记。

What is a Monad?

What is a Monad?

A monad is a parametric type M[T] with two operations, flatMap and unit, that have to satisfy some laws.

这里是说Monad是一类特殊的类型,它有两个方法, flatMap和unit,这两个方法必须满足一些约束。嗯……并不是说任何有flatMap和unit方法的类都是Monad, Monad的这两个方法是有特定语义的,也正是这两个方法的语义使一个类型成为Monad.

用Scala来定义Monad如下:

trait M[T] {
    def flatMap[U](f: T => M[U]): M[U]
}

def unit[T](x: T): M[T]

如果Monad被定义为一个trait,那么unit方法并不是这个trait的一部分,而是独立于Monad对象的。我觉得,可以理解为unit是一个生成Monad对象的方法,它和具体的对象没有关系。


Examples of Monads

  • List is a monad with unit(x) = List(x)
  • Set is a monad with unit(x) = Set(x)
  • Option is a monad with unit(x) = Some(x)
  • Generator is a monad with unix(x) = single(x)     注:Generator是Cousera的这门课程里的上一小节里讲到的一个类

flatMap is an operation on each of these types, whereas unit in Scala is different for each Monad.

实际上,List Set Option和Generator都可以看成集合,从这些集合的unit方法的定义可以看出,unit就是简单的对一个元素进行包装,使其成为一个类似于“单元素集合”的东西。


Monad and map

map can be defined for every monad as a combination of flatMap and unit:

m map f == m flatMap (x => unit(f(x)))

      == m flatMap( f andThen unit)


实际上map的这种定义已经限制了flatMap和unit的语义。

m map f, 对于上边提到的Monad的语义都是把m中的每个元素通过f映射为另一个monad中的一个元素。因此,若想m map f  == m flatMap( x => unit(f(x))), unit和语义和flatMap的语义就得配合起来。也就是说,对于f(x),经过unit的处理,再经过flatMap的处理,还是f(x).

因此定义flatMap的重点在于如何把一组Monad组合成一个Monad。对于List,这个组合方法就是 ++, 对于Generator,就是

def flatMap[S](f: T => Generator[S]): Generator[S]  = new Generator[S]{
    def generate = f(self.generate).generate
}

即将每个元素运用f,生成一组Monad, 然后取这一组Monad中每个Monad的第一个元素组成一个Monad.

假如,我们定义一种Monad: 一串山楂。 0000

定义unit为:把一个山楂 0 拿一个竹签串起来,让它成为”一串山楂“。 0

定义g为把一个山楂变为一串山楂,具体怎么变由g的定义决定。 可见unit函数是g函数的一种。

定义flatMap(g)为把此山楂串的每个山楂应用g,得到很多串山楂,然后把这些山楂串 串成一整个山楂串。

定义函数f为把每个山楂包上糖。即从 0 变成 @

由以上定义可以推出:

m map f就是把m里的每个山楂都包上糖,再把这些山楂直接串起来,变成  @@@@

那么 x => unit(f(x))就是把一串山楂里的每一个0拿出来,先包上糖变成@,然后用一个竹签把这一个糖山楂串起来。 @

那么flatMap(x => unit(f(x)))就是就是把每个山楂应用 x => unit(f(x)),形成很多只有一个山楂的山楂串  @  @  @  @  , 然后把它们串到一起,变成  @@@@ , 也就等于 m map f

在以上的定义下 m map f  == m flatMap(x => unit(f(x)))


Monad Laws

To qualify a monad, a type has to satisfy three laws:

Associativity:

  m flatMap  f  flatMap g == m flatMap( x => f(x) flatMap g)

Left unit

  unit(x) flatMap f == f(x)

Right Unit

  m flatMap unit == m

这个结合律理解起来有点困难,得先回忆一下flatMap的定义

def flatMap[U](f: T => M[U]): M[U]

这里重点留意下定义里跟数据类型有关的部分。

f: T => M[U] 因此,f是一种Monad的生成器。如果把f用于map,即 m map f, 结果的类型会是M[M[U]],但是m flatMap f, 结果是M[U]。这就是flatMap神奇的地方,m flatMap f得到的值的类型跟m的类型是一样的(都是Monad)。比如,我们定义Int类型有个方法是^2,是求平方,那么就可以 3 ^2 ^2 ^2这样一直算下去,这样就可以很方便地利用递归。比如^6就可以在递归中用^2算出来。因此flatMap的这种对类型的保持,使得可以对Monad做无限的的flatMap,仍然得到Monad。也就是使得所有对Monad的操作都适用于m flatMap f。

也就是说我们可以m flatMap f flatMap g,也可以m flatMap f flatMap g flatMap k这样一直进行下去。

那么

Associativity:

  m flatMap  f  flatMap g == m flatMap( x => f(x) flatMap g)

的意义何在?

Associativity says essentially that one can "inline" nested for expressions:

    for(y <- for (x <-m; y <- f(x))yield y

      z <- g(y)) yield z

==  for( x <- m

      y <- f(x)

      z <- g(y)) yield z

来证明一下:

  for( y <- for( x <- m; y <- f(x)) yield y

    z <- g(y)) yield z

令 k = for(x <-m; y <- f(x)) yield y

原式变成for(y <- k

      z <- g(y)) yield z

== k flatMap (y => for( z <- g(y) yield z)

== k flatMap ( y => (g(y) map ( z => z)))

== k flatMap ( y => g(y))

== k flatMap g

而 k = for(x <- m; y <- f(x)) yield y

  = m flatMap ( x => (for( y <- f(x) yield y))

  = m flatMap ( x => f(x))

  = m flatMap f

所以

for( y <- for( x <- m; y <- f(x)) yield y

    z <- g(y)) yield z

== m flatMap f flatMap g

 for( x <- m

      y <- f(x)

      z <- g(y)) yield z

== m flatMap (x => (for (y <- f(x); z <- g(y)) yield z)

== m flatMap (x => f(x) flatMap( y => g(y))

== m flatMap (x => f(x) flatMap g)

所以,这两个for循环相等,要求 m flatMap f flatMap g == m flatMap(x => f(x) flatMap g), 也就是要求结合律成立。

从另一个角度看这回事,m flatMap f flatMap g就是连续做了两次flatMap。 而 f flatMap(x => f(x) flatMap g),令k = (x => f(x) flatMap g)), 即m flatMap f flatMap g == f flatMap k, 即我们可以用 x => f(x) flatMap g 构造一个新的函数k,使得 m flatMap f flatMap g == f flatMap g。

时间: 2024-09-30 16:31:52

Monad学习的相关文章

Haskell学习-monad

原文地址:Haskell学习-monad 什么是Monad Haskell是一门纯函数式的语言,纯函数的优点是安全可靠.函数输出完全取决于输入,不存在任何隐式依赖,它的存在如同数学公式般完美无缺.可是纯函数因为隔绝了外部环境,连最基本的输入输出都无法完成.而 Monad 就是 Haskell 给出的解决方案.但Monad 并不仅仅是 IO 操作的抽象,它更是多种类似操作之间共性的抽象.所以 Monad 解决的问题并不局限在 IO 上,像 Haskell 中的 Maybe 和 [] 都是 Mona

泛函编程(27)-泛函编程模式-Monad Transformer

经过了一段时间的学习,我们了解了一系列泛函数据类型.我们知道,在所有编程语言中,数据类型是支持软件编程的基础.同样,泛函数据类型Foldable,Monoid,Functor,Applicative,Traversable,Monad也是我们将来进入实际泛函编程的必需.在前面对这些数据类型的探讨中我们发现: 1.Monoid的主要用途是在进行折叠(Foldable)算法时对可折叠结构内元素进行函数施用(function application). 2.Functor可以对任何高阶数据类型F[_]

Monad / Functor / Applicative 浅析

前言 Swift 其实比 Objective-C 复杂很多,相对于出生于上世纪 80 年代的 Objective-C 来说,Swift 融入了大量新特性.这也使得我们学习掌握这门语言变得相对来说更加困难.不过一切都是值得的,Swift 相比 Objective-C,写出来的程序更安全.更简洁,最终能够提高我们的工作效率和质量. Swift 相关的学习资料已经很多,我想从另外一个角度来介绍它的一些特性,我把这个角度叫做「烧脑体操」.什么意思呢?就是我们专门挑一些比较费脑子的语言细节来学习.通过「烧

Promise是Monad吗?

译者按: 近年来,函数式语言的特性都被其它语言学过去了. 原文: Functional Computational Thinking?-?What is a monad? 译者: Fundebug 为了保证可读性,本文采用意译而非直译.另外,本文版权归原作者所有,翻译仅用于学习. 如果你使用函数式编程,不管有没有用过函数式语言,在某总程度上已经使用过Monad.可能大多数人都不知道什么叫做Monad.在这篇文章中,我不会用数学公式来解释什么是Moand,也不使用Haskell,而是用JavaSc

Functor、Applicative 和 Monad

Functor.Applicative 和 Monad 是函数式编程语言中三个非常重要的概念,尤其是 Monad ,难倒了不知道多少英雄好汉.事实上,它们的概念是非常简单的,但是却很少有文章能够将它们描述清楚,往往还适得其反,越描越黑.与其它文章不同的是,本文将从结论出发,层层深入,一步步为你揭开它们的神秘面纱. 说明:本文中的主要代码为 Haskell 语言,它是一门纯函数式的编程语言.其中,具体的语法细节,我们不需要太过关心,因为这并不影响你对本文的理解. 结论 关于 Functor.App

函数式编程-将Monad(单子)融入Swift

前言 近期又开始折腾起Haskell,掉进这个深坑恐怕很难再爬上来了.在不断深入了解Haskell的各种概念以及使用它们去解决实际问题的时候,我会试想着将这些概念移植到Swift中.函数式编程范式的很多概念在Swift等主打面向对象范式的语言中就像各种设计模式一样,优雅地帮助我们构建好整个项目,促使我们的代码更加的美观优雅.安全可靠. 本篇文章为"函数式编程"系列中的第二篇,我主要说下Monad的一些小概念,以及试图将Monad融入Swift中来让其为我们的实际工程项目作出贡献. 关于

Vue.js学习笔记:属性绑定 v-bind

v-bind  主要用于属性绑定,Vue官方提供了一个简写方式 :bind,例如: <!-- 完整语法 --> <a v-bind:href="url"></a> <!-- 缩写 --> <a :href="url"></a> 绑定HTML Class 一.对象语法: 我们可以给v-bind:class 一个对象,以动态地切换class.注意:v-bind:class指令可以与普通的class特

Java多线程学习(吐血超详细总结)

林炳文Evankaka原创作品.转载请注明出处http://blog.csdn.net/evankaka 目录(?)[-] 一扩展javalangThread类 二实现javalangRunnable接口 三Thread和Runnable的区别 四线程状态转换 五线程调度 六常用函数说明 使用方式 为什么要用join方法 七常见线程名词解释 八线程同步 九线程数据传递 本文主要讲了java中多线程的使用方法.线程同步.线程数据传递.线程状态及相应的一些线程函数用法.概述等. 首先讲一下进程和线程

微信小程序学习总结(2)------- 之for循环,绑定点击事件

最近公司有小程序的项目,本人有幸参与其中,一个项目做下来感觉受益匪浅,与大家做下分享,欢迎沟通交流互相学习. 先说一下此次项目本人体会较深的几个关键点:微信地图.用户静默授权.用户弹窗授权.微信充值等等. 言归正传,今天分享我遇到的关于wx:for循环绑定数据的一个tips:  1. 想必大家的都知道wx:for,如下就不用我啰嗦了: <view class="myNew" wx:for="{{list}}">{{item.title}}<view