Either a b 和Future a b 都能够表达失败/成功。在处理异步计算时,通常最好使用Future a b 而不是Future a (Either b c)。更简单、更扁平的类型需要更少的映射:S.map (f) 而不是S.map (S.map (f))。另一个优点是错误值总是在同一个地方,而Future a (Either b c) 则a 和b 都表示计算失败。
不过,我们可能已经有了一个返回 any 的验证函数。例如:
// validateEmail :: String -> Either String String
const validateEmail = s =>
s.includes ('@') ? S.Right (S.trim (s)) : S.Left ('Invalid email address');
如果我们有一个fut 类型为Future String String 的值,我们如何验证fut 可能包含的电子邮件地址?首先要尝试的总是S.map:
S.map (validateEmail) (fut) :: Future String (Either String String)
最好避免这种嵌套。为此,我们首先需要定义一个从Either a b 到Future a b 的函数:
// eitherToFuture :: Either a b -> Future a b
const eitherToFuture = S.either (Future.reject) (Future.resolve);
我们现在可以将一个返回要么返回的函数转换为返回未来的函数:
S.compose (eitherToFuture) (validateEmail) :: String -> Future String String
让我们回顾一下我们对S.map的使用:
S.map (S.compose (eitherToFuture) (validateEmail)) (fut) :: Future String (Future String String)
我们仍然有嵌套,但现在内部和外部类型都是Future String _。这意味着我们可以将S.map 替换为S.chain 以避免引入嵌套:
S.chain (S.compose (eitherToFuture) (validateEmail)) (fut) :: Future String String