При написании полностью асинхронной библиотеки для доступа к удаленному сервису (используя Play2.0) я использую Promise
и Validation
для создания неблокирующего вызова, который имеет тип, представляющий ошибочный и действительный результат одновременно.
Promise
происходит от Play2-scala, где Validation
происходит от scalaz.
Итак, вот тип примеров таких функций
- f ::
A => Promise[Validation[E, B]]
- g ::
B => Promise[Validation[E, C]]
Пока все хорошо, теперь, если я хочу их составить, я могу просто использовать тот факт, что Promise
представляет собой flatMap
, поэтому я могу сделать это с пониманием
for (
x <- f(a);
y <- g(b)
) yield y
Хорошо, я решил эту проблему здесь, потому что я не использовал повторно результаты Validation
в for-comprehension. Итак, если я хочу повторно использовать x
в g
, вот как я могу это сделать
for (
x <- f(a); // x is a Validation
y <- x.fold(
fail => Promise.pure(x),
ok => g(ok)
)
) yield y
Справедливо, но такой шаблон будет снова и снова загрязнять мой код. Проблема здесь в том, что у меня есть своего рода двухуровневая монадическая структура, такая как M[N[_]]
.
На данном этапе существует ли какая-либо структура в f°-программировании, позволяющая работать с такой структурой, легко пропуская второй уровень:
for (
x <- f(a); //x is a B
y <- g(b)
) yield y
Теперь ниже показано, как я добился чего-то подобного.
Я создал некую монадическую структуру, которая объединяет два уровня в один, скажем, ValidationPromised
, который упростил тип Promise
двумя методами:
def /~> [EE >: E, B](f: Validation[E, A] => ValidationPromised[EE, B]): ValidationPromised[EE, B] =
promised flatMap { valid =>
f(valid).promised
}
def /~~>[EE >: E, B](f: A => ValidationPromised[EE, B]): ValidationPromised[EE, B] =
promised flatMap { valid =>
valid.fold (
bad => Promise.pure(KO(bad)),
good => f(good).promised
)
}
Это позволяет мне делать такие вещи
endPoint.service /~~> //get the service
(svc => //the service
svc.start /~~> (st => //get the starting elt
svc.create(None) /~~> //svc creates a new elt
(newE => //the created one
newEntry.link(st, newE) /~~> //link start and the new
(lnk => Promise.pure(OK((st, lnk, newE)))) //returns a triple => hackish
)
)
)
Как мы видим, /~~>
очень похоже на flatMap
, но пропускает один уровень. Проблема заключается в многословии (вот почему в Scala существует «для понимания», а в Haskell — «делать»).
Еще один момент, у меня есть /~>
, который также стоит как map
, но работает на втором уровне (вместо действительного типа -- третьего уровня)
Итак, мой второй вопрос является следствием первого... Приближаюсь ли я к устойчивому решению этой конструкции?
извините, что так длинно
IO
вместоPromise
. Следовательно, у меня будет тот же шаблон, то есть:IO[Validation[E, A]]
- person Andy Petrella   schedule 10.06.2012