Before we introduce what is Monad, first let's recap what is a pointed functor:

A pointed functor is a Functor with .of() method

Why pointed Functor is imporant? here

OK, now, let's continue to see some code:

const mmo = Maybe.of(Maybe.of('nunchucks'));
// Maybe(Maybe('nunchucks'))

We don't really want nested Functor, it is hard for us to work with, we need to remember how deep is the nested Functor.

To solve the problem we can have a new method, call '.join()'.

mmo.join();
// Maybe('nunchucks')

What '.join()' does is just simply reduce one level Functor.

So how does implememation of 'join()' looks like?

Maybe.prototype.join = function join() {
return this.isNothing() ? Maybe.of(null) : this.$value;
};

As you can see, we just return 'this.$value', instead of put the value into Maybe again.

With those in mind, let's define what is Monad!

Monads are pointed functors that can flatten

Let's see a example, how to use join:

// join :: Monad m => m (m a) -> m a
const join = mma => mma.join(); // firstAddressStreet :: User -> Maybe Street
const firstAddressStreet = compose(
join,
map(safeProp('street')),
join,
map(safeHead), safeProp('addresses'),
); firstAddressStreet({
addresses: [{ street: { name: 'Mulburry', number: }, postcode: 'WC2N' }],
});
// Maybe({name: 'Mulburry', number: 8402})

For now, each map opreation which return a nested map, return call 'join' after.

Let's abstract this into a function called chain.

// chain :: Monad m => (a -> m b) -> m a -> m b
const chain = curry((f, m) => m.map(f).join()); // or // chain :: Monad m => (a -> m b) -> m a -> m b
const chain = f => compose(join, map(f));

Now we can rewrite the previous example which .chain():

// map/join
const firstAddressStreet = compose(
join,
map(safeProp('street')),
join,
map(safeHead),
safeProp('addresses'),
); // chain
const firstAddressStreet = compose(
chain(safeProp('street')),
chain(safeHead),
safeProp('addresses'),
);

To get a feelings about chain, we give few more examples:

// getJSON :: Url -> Params -> Task JSON
getJSON('/authenticate', { username: 'stale', password: 'crackers' })
.chain(user => getJSON('/friends', { user_id: user.id }));
// Task([{name: 'Seimith', id: 14}, {name: 'Ric', id: 39}]); // querySelector :: Selector -> IO DOM
querySelector('input.username')
.chain(({ value: uname }) => querySelector('input.email')
.chain(({ value: email }) => IO.of(`Welcome ${uname} prepare for spam at ${email}`)));
// IO('Welcome Olivia prepare for spam at olivia@tremorcontrol.net'); Maybe.of()
.chain(three => Maybe.of().map(add(three)));
// Maybe(5); Maybe.of(null)
.chain(safeProp('address'))
.chain(safeProp('street'));
// Maybe(null);

Theory

The first law we'll look at is associativity, but perhaps not in the way you're used to it.

// associativity
compose(join, map(join)) === compose(join, join);

These laws get at the nested nature of monads so associativity focuses on joining the inner or outer types first to achieve the same result. A picture might be more instructive:

The second law is similar:

// identity for all (M a)
compose(join, of) === compose(join, map(of)) === id;

It states that, for any monad Mof and join amounts to id. We can also map(of) and attack it from the inside out. We call this "triangle identity" because it makes such a shape when visualized:

Now, I've seen these laws, identity and associativity, somewhere before... Hold on, I'm thinking...Yes of course! They are the laws for a category. But that would mean we need a composition function to complete the definition. Behold:

const mcompose = (f, g) => compose(chain(f), g);

// left identity
mcompose(M, f) === f; // right identity
mcompose(f, M) === f; // associativity
mcompose(mcompose(f, g), h) === mcompose(f, mcompose(g, h));

They are the category laws after all. Monads form a category called the "Kleisli category" where all objects are monads and morphisms are chained functions. I don't mean to taunt you with bits and bobs of category theory without much explanation of how the jigsaw fits together. The intention is to scratch the surface enough to show the relevance and spark some interest while focusing on the practical properties we can use each day.

More detail

[Functional Programming] Monad的更多相关文章

  1. [Functional Programming Monad] Refactor Stateful Code To Use A State Monad

    When we start to accumulate functions that all work on a given datatype, we end up creating a bunch ...

  2. [Functional Programming Monad] Apply Stateful Computations To Functions (.ap, .liftA2)

    When building our stateful computations, there will come a time when we’ll need to combine two or mo ...

  3. [Functional Programming Monad] Combine Stateful Computations Using Composition

    We explore a means to represent the combination of our stateful computations using familiar composit ...

  4. [Functional Programming Monad] Combine Stateful Computations Using A State Monad

    The true power of the State ADT really shows when we start combining our discrete, stateful transact ...

  5. [Functional Programming Monad] Modify The State Of A State Monad

    Using put to update our state for a given state transaction can make it difficult to modify a given ...

  6. [Functional Programming Monad] Substitute State Using Functions With A State Monad (get, evalWith)

    We take a closer look at the get construction helper and see how we can use it to lift a function th ...

  7. [Functional Programming Monad] Map And Evaluate State With A Stateful Monad

    We explore our first stateful transaction, by devising a means to echo our state value into the resu ...

  8. Functional Programming without Lambda - Part 2 Lifting, Functor, Monad

    Lifting Now, let's review map from another perspective. map :: (T -> R) -> [T] -> [R] accep ...

  9. Monad (functional programming)

    In functional programming, a monad is a design pattern that defines how functions, actions, inputs, ...

随机推荐

  1. Docker应用系列(四)| 部署java应用

    本示例基于Centos 7,假设目前使用的账号为release,拥有sudo权限. 由于Docker官方镜像下载较慢,可以开启阿里云的Docker镜像下载加速器,可参考此文进行配置. 主机上服务安装步 ...

  2. python 学习笔记 - Queue & Pipes,进程间通讯

    上面写了Python如何创建多个进程,但是前面文章中创建的进程都是哑巴和聋子,自己顾自己执行,不会相互交流.那么如何让进程间相互说说话呢?Python为我们提供了一个函数multiprocessing ...

  3. FastReport.Net使用:[38]关系的使用

    打印所有成绩 1. 数据源准备 接下来我们需要打印学生成绩,而成绩表中无姓名,我们通过建立Realtion关系来打印数据. 2. 创建Relation关系 在数据视图上的动作下拉菜单中选择“新建关系” ...

  4. HDU 1213(并查集)

    How Many Tables Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)T ...

  5. BZOJ4530 BJOI 2014 大融合

    对LCT子树大小进行维护. size表示实子树大小,sz表示虚子树大小. 具体操作是体现在link和splay中,可以看代码. 注意每次做完都要update. By:大奕哥 #include<b ...

  6. Mybatis分页插件PageHelper的实现

    Mybatis分页插件PageHelper的实现 前言 分页这个概念在做web网站的时候很多都会碰到 说它简单吧 其实也简单 小型的网站,完全可以自己写一个,首先查出数据库总条数,然后按照分页大小分为 ...

  7. 线性DP POJ 1159 Palindrome

    Palindrome Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 59101   Accepted: 20532 Desc ...

  8. 线段树--codevs 1690 开关灯

    codevs 1690 开关灯 USACO  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 钻石 Diamond 题目描述 Description YYX家门前的街上有N(2& ...

  9. noip模拟 五子棋

    递推+模拟.在读取数据时,我们建4个图,分别代表这个图中横.纵.左斜右斜的连续长度.例如heng[i][j]代表ij这个点所在的横着一条线的长度. 然后搜索,对于一个空点,如果他的上下都>=4那 ...

  10. 2015 UESTC 数据结构专题H题 秋实大哥打游戏 带权并查集

    秋实大哥打游戏 Time Limit: 1 Sec  Memory Limit: 256 MB 题目连接 http://acm.uestc.edu.cn/#/contest/show/59 Descr ...