对于大多数刚刚入门函数式编程的同学来说,monad(单子、又叫单体)可能是这里面的一道坎。你可能对 map 、 flatMap 以及 filter 再熟悉不过,可是到了高阶的抽象层次上就又会变得一脸懵逼。其实每个人在学习的阶段都会经历这个过程,不过希望这篇文章能让你重新理解 monad 以及其他相关的概念。

Optional

Swift 作为一门类型安全的强类型语言,它在编译阶段就会对你的数据类型进行比较多的检查。因此,在 Swift 中我们遇到了一种新的数据类型,叫做 Optional 。它的定义如下:

Optional 是个枚举类型,可以看到它有两个值: none 以及 some 。简单点讲:

值要么存在(presence),要么不存在(absence)。

这也就意味着 Optional 可能是包含了一个某个类型的值( some ),也可能是什么都没有( none )。在这里,Optional 就是个容器( container )。

对于基础类型,在其后面直接加上 ? 就代表了这就是个可选值类型(Optional value)。默认值即为 Optional 的预设值 nil (区别于 Objective-C)。

判断一个可选值是否为空,我们通常会采用 if let 的写法来解包(wrap an optional)。

Map

基础类型的 map 函数调用是非常简单的:

如果对于 Optional 呢?

我们发现, map 函数作用在 Optional 上时:

  • 值存在( .some ):值类型 Optional(Int) ,返回值类型 Optional(Int) ;
  • 值不存在( .none ):返回值等同输入( nil )。

map 函数定义在 Optional 上,最大的好处就在于空值( nil )的处理,不需要我们再去使用 if let 解包(空值并没有乘法运算):

因此:

map 只对值存在的可选值进行处理。

map 通常的表示方法:

在这里,容器 Container 就相当于 Optional ,泛型 T 和 U 均为 Int 类型。在处理完 Int 值后 map 函数就把 Int 型转换成了 Optional(Int) ,并返回。

Async Callback Trouble

处理异步的网络请求是一件痛苦的事情。你一定碰到过:

为什么我的异步回调就没有一种方式能够告诉我在哪里出错了呢?方案其实也很简单,我们先在定义一下异步处理的结果( Result ):

有没有发现 Result 类型很像 Optional ?没错。它能包含一个成功的返回值;也能在没有返回值时提供一个错误消息。

我们也同时希望 map 能帮我们处理 Result :如果有结果,就从 JSON 转换到 String 、再转换到其他类型;否则返回错误信息。

这样的 map 函数怎么写呢?不妨先来看一下:

举个最基本的例子,我们希望将返回的 JSON 转换成 String ,那在这里, map 所接受的高阶变换 f 就是一个 JSON -> String 的函数。调用时, Result<JSON> 就会通过 map 最终转换成 Result<String> 类型。

看上去很不错!

Functor

在了解 monad 之前,我们先来了解一下它的孪生兄弟:functor(函子)。

从上面的例子中可以看到,在调用 map 函数后,我们还会把 String 类型的结果封装成了一个可选值 Result<String> 。

像这样能够从容器(Container,这里即 Result )中取出元素,并通过某个函数将其转换成可以再次被容器包装的结果的类型就称之为 functor。

还有些不懂?没事,暂时就先记住有 functor 这么个玩意儿。

FlatMap

重新回到之前 JSON -> String 的例子上来。假设我们已经将某个 json 转换成了字符串,现在需要将字符串重新格式化,那我们应该需要再调用一次 map :

不过我们多么希望返回的结果是个 Result<String> 的类型。不如写一个函数来解包带有两层的 Result<T> 。

还有一点,在写 flatten 函数的时候,我们也同时考虑了在 map 函数中出现转换失败的问题。转换正确的时候的确我们的 map 的输出是个 String 类型的值,随之输出 Result<String> 进入下一层的 map ;如果失败,则应当是被转换成 .failure 的结果。

将 map 和 flatten 结合一下,我们就得到了所谓的 flatMap (又称作 bind ):

通过 flatMap 我们可以非常轻松地处理中途出现的错误异常,并对给定类型进行多次连续的类型转换。

Monad

最后再来说什么是 monad。Chris Edihof 曾在他的文章中指出:

如果可以为某个类型定义它的 flatMap 方法,那么这个类型通常就是个 monad 。(If you can define flatMap for a type, the type is often called a monad .)

在这里,我们通过 map 和 flatten 实现了 Result 类型的 flatMap 。此时,我们就可以说 Result 这个类型就是一个 monad。

Deal with Monad

到现在你就可以非常轻松地处理你的异步请求了。

Summary

  • 重新回顾一下 map 和 flatMap 在 Result<T> 上的工作方式:

  • Functor、monad 可以看作是一种运算的抽象。它们的目的都是为了更好的解决类型的封装和转换。

Further Reading

  • ReactiveCocoa

  • Promise & Future

  • Swift 中的 throw 及 rethrow

References

  1. Functor and Monad in Swift - Javier Soto
  2. Swift 烧脑体操(五)- Monad - 唐巧
  3. Monads Everywhere: Porting C#’s Tasks to Swift - Nevyn Bengtsson
  4. Monads in Swift - Chris Edihof
  5. 続・ゲンバのSwift

来自:http://blog.cee.moe/recap-monad.html

http://www.open-open.com/lib/view/open1478588965804.html

重新理解 Monad的更多相关文章

  1. Atitit 理解Monad attilax总结

    Atitit 理解Monad attilax总结 但函数式编程最大的一个问题是,函数是一个数学抽象,在现实世界中不存在,1 那既然这样就够用了,还要 Monad 干嘛?Monad 的作用在这里就体现出 ...

  2. Monad / Functor / Applicative 浅析

    前言 Swift 其实比 Objective-C 复杂很多,相对于出生于上世纪 80 年代的 Objective-C 来说,Swift 融入了大量新特性.这也使得我们学习掌握这门语言变得相对来说更加困 ...

  3. Monad详解

    最近几年,函数式编程变得越来越流程,其代码简洁.副作用小.维护成本低等特点,使得许多其它的语言也开始支持函数式编程,比如Java 和 C#等.本文主要介绍一下函数式编程中的一个重要概念:Monad. ...

  4. fir.im Weekly - Stanford 的 Swift 课程来了

    上周提过,Swift 的 Github 主页上已经有了 >>「Port to Android」,这周重点推荐一下 Stanford 的 Swift 课程. Developing iOS 9 ...

  5. 《JavaScript ES6 函数式编程入门经典》--推荐指数⭐⭐⭐

    这本书比较基础认真看完再自己写点demo一个双休日就差不多, 总体来说看完还是有收获的,会激起一些你对函数编程的兴趣 主要目录如下: 第1章 函数式编程简介 11.1 什么是函数式编程?为何它重要 1 ...

  6. 我所理解的monad(4):函子(functor)是什么--可把范畴简单的看成高阶类型

    大致介绍了幺半群(monoid)后,我们重新回顾最初引用wadler(haskell委员会成员,把monad引入haskell的家伙)的那句话: 现在我们来解读这句话中包含的另一个概念:自函子(End ...

  7. monad重新理解

    monad是高阶抽象类型: 包含类型构造器: monad抽象的核心是类型封装和类型转化(map). 实现monad的的类型必须实现(基础)类型的封装和类型转化的功能: 在此基础上实现其他的功能(基本依 ...

  8. 我所理解的monad(1):半群(semigroup)与幺半群(monoid)

    google到数学里定义的群(group): G为非空集合,如果在G上定义的二元运算 *,满足 (1)封闭性(Closure):对于任意a,b∈G,有a*b∈G (2)结合律(Associativit ...

  9. 怎样理解Functor与Monad

    1. 复合函数操作符 Prelude> :t (.) (.) :: (b -> c) -> (a -> b) -> a -> c Prelude> (.) ( ...

随机推荐

  1. [bzoj4282]慎二的随机数列_动态规划_贪心

    慎二的随机数列 bzoj-4282 题目大意:一个序列,序列上有一些数是给定的,而有一些位置上的数可以任意选择.问最长上升子序列. 注释:$1\le n\le 10^5$. 想法:结论:逢N必选.N是 ...

  2. 洛谷——P1505 苹果摘陶陶

    题目背景 根据2005年的Noip普及组第一题衍生出的一题. 但是有一点点的恶搞成分在里面..... 题目描述 话说去年苹果们被陶陶摘下来后都很生气,于是就用最先进的克隆技术把陶陶克隆了好多份> ...

  3. Spring MVC-处理程序映射(Handler Mapping)-Bean名称Url处理程序映射(Bean Name Url Handler Mapping)示例(转载实践)

    以下内容翻译自:https://www.tutorialspoint.com/springmvc/springmvc_beannameurlhandlermapping.htm 说明:示例基于Spri ...

  4. JVM内存管理和垃圾回收机制介绍

    http://backend.blog.163.com/blog/static/20229412620128233285220/     内存管理和垃圾回收机制是JVM最核心的两个组成部分,对其内部实 ...

  5. C++对象模型——对象复制语意学 (Object Copy Semantics)(第五章)

    5.3    对象复制语意学 (Object Copy Semantics) 当设计一个 class,并以一个 class object指定给 class object时,有三种选择:     1.什 ...

  6. java如何实现替换指定位置的指定字符串的功能

    /**  * @创建日期 2013-07-15  * @创建时间 14:25:59  * @版本号 V 1.0  */ public class CosTest {     public static ...

  7. 【JavaScript】离开页面前提示

    离开页面前的提示不能够用onunload去做,由于它仅仅是兼容IE,你要兼容Google与FireFox就蛋疼了. 并且这个事件还是关闭之后才会触发的. 取而代之能够用onbeforeunload去实 ...

  8. 使用c3p0与DBCP连接池,造成的MySql 8小时问题解决方式

    本文提供了对c3p0与DBCP连接池连接MySql数据库时. 8小时内无请求自己主动断开连接的解决方式.首先介绍一下我在项目(c3p0连接池)中遇到的问题,后面还提供了使用DBCP连接池的解决方式. ...

  9. LA 4329(树状数组)

    算法竞赛入门经典 p197 题目大意: 一条大街上住着n个乒乓球爱好者.常常比赛切磋技术.每一个人都有一个不同的技能值a[i].每场比赛须要3个人:两名选手,一名裁判.他们有个奇怪的约定,裁判必须住在 ...

  10. Ural 1353 Milliard Vasya&#39;s Function(DP)

    题目地址:Ural 1353 定义dp[i][j].表示当前位数为i位时,各位数和为j的个数. 对于第i位数来说.总能够看成在前i-1位后面加上一个0~9.所以状态转移方程就非常easy出来了: dp ...