При написании полностью асинхронной библиотеки для доступа к удаленному сервису (используя Play2.0) я использую Promise
и Validation
для создать неблокирующий вызов, который имеет тип, представляющий ошибочный и действительный результат одновременно.
Promise
происходит от Play2-scala, а Validation
происходит от scalaz.
Итак, вот тип примеров таких функций
A => Promise[Validation[E, B]]
B => Promise[Validation[ E, C]]
Пока все хорошо, теперь, если я хочу составить их, я могу просто использовать тот факт, что Promise
представляет собой flatMap
, так что я могу сделать это с for-comprehension
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 — «делать»).
Еще момент, у меня есть /~>
, который стоит как карта
тоже, но работает на втором уровне (вместо действительного типа -- третьегоуровень)
Итак, мой второй вопрос является следствием первого... Приближаюсь ли я к устойчивому решению этой конструкции?
извините, что так длинно