资讯专栏INFORMATION COLUMN

[译]浅入浅出Monads

philadelphia / 1286人阅读

摘要:接受另一个函数作为参数,然后用接受的这个新函数处理,将结果再次传给,最后将实例化的新对象返回。空值检查就是个不错的例子这个实现里,只在为合法值非空时,传入。但不论怎么变化,她们也都和一样遵守上面提到的规则。

大多数关于monad的教程都和老太太的裹脚布一样,又臭、又长,说不清、道不明。当然我也不伟大,没法保证我写的一定更明了,更生动,甚至更屌?不过我至少可以确定,我这篇更简洁。浪费不了你多少时间的!

废话不多说,先看下面这个对象Foo。她就是个monad。你必定会吃惊道:我擦,这是什么意思?不要急,故事要从头说,我们还是先来分析下Foo到底是怎么干活的:

function Foo(value) {
    this.get = ()=> value;
    this.map = fn => {
        let result = fn(value);
        return new Foo(result);
    };
}

Foo接受了一个value,而且一直都没改变她的值。Foo里提供了一个get方法使得外部调用者可以获取value,还有一个牛逼的方法叫mapmap接受另一个函数(handler)作为参数,然后用接受的这个新函数handler处理value,将结果再次传给Foo,最后将实例化的新Foo对象返回。

因为map返回一个Foo的实例,于是map的方法是可以被链式调用的:

let one = new Foo(1);
let two = one.map(x => x + 7).map(x => x / 2).map(x => x - 2);
two.get() === 2;

链式调用high不 high?她允许我们可以按照期望,对x执行顺序操作,这种更“自然”的风格绝对比下面这种疯狂嵌套的风格要好:

//嵌套组合的方式长这个样子,
//我们必须从右向左读,才能得出结论
//而且你说实话,这风格,你喜欢么?
let two = minusTwo(divideByTwo(addSeven(1)));

而且每一个步骤里处理value到下一个Foo实例的逻辑我们都可以抽离出去。

再来看看另一个monad,我们姑且称之为Bar吧:

function Bar(value) {
  this.get = ()=> value;
  this.map = fn => {
      let result = fn(value);
      console.log(result);
      return new Bar(result);
  };
}

如果这时候我有一系列操作想顺序作用在value上,而且还要在每次变化时打印出来新的value,就可以利用Bar把下面这种原始的,二逼的代码:

let stepOne = something(1);
console.log(stepOne);
let stepTwo = somethingElse(stepOne);
console.log(stepTwo);
let stepThree = somethingDifferent(stepTwo);
console.log(stepThree);

重构成下面这种优雅的,高端的样子了:

new Bar(1)
  .map(something)           // console >> logs new value
  .map(somethingElse)       // console >> logs new value
  .map(somethingDifferent); // console >> logs new value

现在你应该懂什么是monads。我完成诺言了哦!Monads可以粗略的归纳出下面这些规则:

monad总会包含一个值

monad有一个map方法,而且该方法会接受一个函数(handler)作为参数

map通过上一步提到的handler处理value(还可能有些其他逻辑),并获取其结果

map最后返回一个new [Monad],以完成链式调用

目前能想到的就这些了。如果上述的例子你都理解了,那你就懂什么是Monads了。如果你还再等什么黑魔法或者惊奇算法,那抱歉了,还真没有!

理论上,我们任意修改map的实现,任何可以在各步骤handler之间的逻辑都行 - 例如:决定传什么内容到下一步,或者对上一步handler处理的结果做点儿什么。

空值检查就是个不错的例子:

function Maybe(value) {
  this.get = ()=> value;
  this.map = fn => {
      if (value === null) {
          return new Maybe(null);
      } else {
          return new Maybe(fn(value));
      }
  };
}

这个实现里,map只在value为合法值(非空)时,传入handler。否则就只返回一个自身的copy。利用上面的非空检查的Monad函数Maybe,我们可以把下面这个冗长矬的代码:

let connection = getConnection();
let user = connection ? connection.getUser() : null;
let address = user ? user.getAddress() : null;
let zipCode = address ? address.getZip() : null;

重构成这个样子:

let zipCode =
    new Maybe(getConnection())
    .map(c => c.getUser())
    .map(u => u.getAddress())
    .map(a => a.getZip());

//最后得到的要么是真正的zipCode(每一步都正确处理)
//要么就是个null
zipCode.get();

希望今天的说教已经说明了monads和她的map方法为什么这么牛逼了!关于这点,我倒是不怀疑你也能自己想出来^^

还有其他很多种monads,都有不同的用法和用途。但不论怎么变化,她们也都和FooBar一样遵守上面提到的规则。

掌握了这些技巧,你基本就可以装做一个会写函数式的“牛人”了!

原文地址:Monads Explained Quickly

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/79149.html

相关文章

  • SegmentFault 技术周刊 Vol.16 - 浅入浅出 JavaScript 函数式编程

    摘要:函数式编程,一看这个词,简直就是学院派的典范。所以这期周刊,我们就重点引入的函数式编程,浅入浅出,一窥函数式编程的思想,可能让你对编程语言的理解更加融会贯通一些。但从根本上来说,函数式编程就是关于如使用通用的可复用函数进行组合编程。 showImg(https://segmentfault.com/img/bVGQuc); 函数式编程(Functional Programming),一...

    csRyan 评论0 收藏0
  • 端到端测试神器 cypress 浅入浅出

    摘要:我之前写过关于的文章,,最近在工作中用到比较多了,顿时觉得确实是比较优秀的一个。 我之前写过关于cypress的文章,https://segmentfault.com/a/11...,最近在工作中用到cypress比较多了,顿时觉得cypress确实是比较优秀的一个。 1. 软件安装. 2. 安装cypress 安装cypress客户端:http://download.cypress...

    CarlBenjamin 评论0 收藏0
  • vuex浅入浅出

    摘要:来自不同视图的行为需要变更同一状态。图解后端的行为,响应在上的用户输入导致的状态变化。中的非常类似于事件每个都有一个字符串的事件类型和一个回调函数。 什么是Vuex? Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。 Vuex采用和Redux类似的单向数据流的方式来管理数据。用户...

    琛h。 评论0 收藏0
  • 浅入浅出FlowDroid(一): 简介&基本使用

    摘要:后文将围绕做一些介绍。尽管如此,的使用对新手而言仍然充满了困难。本系列文章基本为个人见解,难免有错误与误解,如有客观错误欢迎提出。 前言 说到Android的污点分析框架,网上的搜索结果大多指向静态的FlowDroid与动态的TaintDroid。尽管由于加固、混淆等技术使得针对Android的静态分析越来越困难,但静态分析的无先验分析能力无法被动态分析取代,使得静态分析仍有发挥空间。...

    wqj97 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<