# 你造 Promise 就是 Monad 吗

Monad 这个概念好难解释, 你可以理解为一个 Lazy 或者是状态未知的盒子. 听起来像是薛定谔猫(估计点进去你会更晕了). 其实就是的, 在你打开这个盒子之前, 你是不知道里面的猫处在那种状态.

### Either

either

``(a -> c) -> (b -> c) -> Either a b -> c``

bind 方法的意思很简单, 就是给这个盒子加一个操作, 比如往盒子在加放射性原子,如果猫活着,就是绿巨猫, 如果猫是死的,那就是绿巨死猫.

``````Left("cat").bind(cat => 'hulk'+cat)
// => Left "hulkcat"

### 走钢索

#### 一般解法

``````eweda.installTo(this);
var landLeft = eweda.curry(function(n, pole){
return [pole[0]+n, pole[1]];
});
var landRight = eweda.curry(function(n, pole){
return landLeft(n, eweda.reverse(pole));
});
var result = eweda.pipe(landLeft(1), landRight(1), landLeft(2))([0,0]);
console.log(result);
// => [3, 1]``````

``````var landLeft = eweda.curry(function(n, pole){
if(Math.abs(pole[0]-pole[1]) > 3)
return [pole[0]+n, pole[1]];
});
var landRight = eweda.curry(function(n, pole){
return landLeft(n, eweda.reverse(pole));
});
var result = eweda.pipe(landLeft(10), landRight(1), landRight(8))([0,0]);
console.log(result);

#### 现在来试试用 Either

``````var land = eweda.curry(function(lr, n, pole){
pole[lr] = pole[lr] + n;
if(Math.abs(pole[0]-pole[1]) > 3) {
return new Left("dead when land " + n + " became " + pole);
}
return new Right(pole);
});

var landLeft = land(0)
var landRight = land(1);``````

``````var stillAlive = function(x){
console.log(x)
}
console.log('皮尔斯' + x);
}

``````var Monad = function(type, defs) {
for (name in defs){
type.prototype[name] = defs[name];
}
return type;
};
function Left(value){
this.value = value
}
function Right(value){
this.value=value;
}

bind:function(fn){
return fn(this.value)
}
})

bind: function(fn){
return this;
}
})
``````

``````either = function(left, right, either){
if(either.constructor.name === 'Right')
return right(either.value)
else
return left(either.value)
}``````

``````var walkInLine = new Right([0,0]);
.bind(landRight(5))
// => [2,5]
.bind(landRight(5))
.bind(landLeft(3))
.bind(landLeft(10)
.bind(landRight(10)))

// => "皮尔斯dead when land 10 became 15,5"``````

1. 一般做法每次都会检查查尔斯挂了没挂, 也就是重复获得之前操作的 context

2. Monad 不对异常做处理, 只是不停地往盒子里加操作. 你可以看到对错误的处理推到了最后取值的 either.

3. Monad 互相传递的只是盒子, 而一般写法会把异常往下传如`"dead"`, 这样导致后面的操作都得先判断这个异常.

comment 由于是用 JavaScript, pole 不限定类型, 所以这里单纯的用字符串代表 pole 的异常状态. 但如果换成强类型的 Java, 可能实现就没这么简单了.

``````var aPromise = \$.ajax({
url: "https://api.github.com/users/jcouyang/gists"
dataType: 'jsonp'
})
aPromise /***
=> Object { state: .Deferred/r.state(),
always: .Deferred/r.always(),
then: .Deferred/r.then(),
promise: .Deferred/r.promise(),
pipe: .Deferred/r.then(),
***/``````

``````anotherPromise = aPromise.then(_ => _.data.forEach(y=> console.log(y.description)))
/* =>
Object { state: .Deferred/r.state(),
always: .Deferred/r.always(),
then: .Deferred/r.then(),
promise: .Deferred/r.promise(),
pipe: .Deferred/r.then(),

"connect cisco anyconnect in terminal"
"为什么要柯里化（curry）"
"批量获取人人影视下载链接"
......
*/``````

``````var ewd = document.createElement('script'); ewd.type = 'text/javascript'; ewd.async = true;
ewd.src = 'https://rawgit.com/CrossEye/eweda/master/eweda.js';

eweda.installTo(this); //安装到 window 上``````
``````var land = curry(function(lr, n, pole){
pole[lr] = pole[lr] + n;
if(Math.abs(pole[0]-pole[1]) > 3) {
return new Promise((resovle,reject)=>reject("dead when land " + n + " became " + pole));
}
return new Promise((resolve,reject)=>resolve(pole));
});

var landLeft = land(0)
var landRight = land(1);

Promise.all([0,0])
.then(landLeft(2), _=>_)
.then(landRight(3), _=>_) // => Array [ 2, 3 ]
.then(landLeft(10), _=>_)
.then(landRight(10), _=>_)
.then(_=>console.log(_),_=>console.log(_))
// => "dead when land 10 became 12,3"``````

#### 你可能感兴趣的文章

1 条评论
Kyle_Feng · 2014年11月19日

oyanglulu

726 声望

#### oyanglulu

build passing or failed

3 人关注