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
представляют собой неудачные вычисления.
Мы можем, тем не менее, уже иметь функцию проверки, которая возвращаетили. Например:
// 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