实际当中,Maybe
最常用在那些可能会无法成功返回结果的函数中。
// safeHead :: [a] -> Maybe(a)
var safeHead = function(xs) {
return Maybe.of(xs[0]);
};
var streetName = compose(map(_.prop('street')), safeHead, _.prop('addresses'));
streetName({addresses: []});
// Maybe(null)
streetName({addresses: [{street: "Shady Ln.", number: 4201}]});
// Maybe("Shady Ln.")
safeHead
与一般的 _.head
类似,但是增加了类型安全保证。引入 Maybe
会发生一件非常有意思的事情,那就是我们被迫要与狡猾的 null
打交道了。safeHead
函数能够诚实地预告它可能的失败——失败真没什么可耻的——然后返回一个 Maybe
来通知我们相关信息。实际上不仅仅是通知 ,因为毕竟我们想要的值深藏在 Maybe
对象中,而且只能通过 map
来操作它。本质上,这是一种由 safeHead
强制执行的空值检查。有了这种检查,我们才能在夜里安然入睡,因为我们知道最不受人待见的 null
不会突然出现。类似这样的 API 能够把一个像纸糊起来的、脆弱的应用升级为实实在在的、健壮的应用,这样的 API 保证了更加安全的软件。
有时候函数可以明确返回一个 Maybe(null)
来表明失败,例如:
// withdraw :: Number -> Account -> Maybe(Account)
var withdraw = curry(function(amount, account) {
return account.balance >= amount ?
Maybe.of({balance: account.balance - amount}) :
Maybe.of(null);
});
// finishTransaction :: Account -> String
var finishTransaction = compose(remainingBalance, updateLedger); // <- 假定这两个函数已经在别处定义好了
// getTwenty :: Account -> Maybe(String)
var getTwenty = compose(map(finishTransaction), withdraw(20));
getTwenty({ balance: 200.00});
// Maybe("Your balance is $180.00")
getTwenty({ balance: 10.00});
// Maybe(null)
要是钱不够,withdraw
就会对我们嗤之以鼻然后返回一个 Maybe(null)
。withdraw
也显示出了它的多变性,使得我们后续的操作只能用 map
来进行。这个例子与前面例子不同的地方在于,这里的 null
是有意的。我们不用 Maybe(String)
,而是用 Maybe(null)
来发送失败的信号,这样程序在收到信号后就能立刻停止执行。这一点很重要:如果 withdraw
失败了,map
就会切断后续代码的执行,因为它根本就不会运行传递给它的函数,即 finishTransaction
。这正是预期的效果:如果取款失败,我们并不想更新或者显示账户余额。