用例

实际当中,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。这正是预期的效果:如果取款失败,我们并不想更新或者显示账户余额。