Асинхронное вычисление с проверкой в ​​Scala с использованием Scalaz

При написании полностью асинхронной библиотеки для доступа к удаленному сервису (используя 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, но работает на втором уровне (вместо действительного типа -- третьего уровня)

Итак, мой второй вопрос является следствием первого... Приближаюсь ли я к устойчивому решению этой конструкции?

извините, что так длинно


person Andy Petrella    schedule 10.06.2012    source источник
comment
Я уже давно собирался использовать ScalaZ со своими приложениями Play, и это хороший толчок для меня. Я дам вам знать, как у меня дела, и, надеюсь, смогу дать здесь содержательный ответ.   -  person opyate    schedule 10.06.2012
comment
Ага! Спасибо. На самом деле настоящая проблема заключается не в использовании ScalaZ с Play. Это более общий вопрос (про f° prog), потому что без Play (поэтому только ScalaZ) я использовал IO вместо Promise. Следовательно, у меня будет тот же шаблон, то есть: IO[Validation[E, A]]   -  person Andy Petrella    schedule 10.06.2012


Ответы (1)


Концепция, которую вы ищете здесь, это преобразователи монад. Вкратце, преобразователи монад компенсируют монады, которые не составляются, позволяя вам "складывать " их.

Вы не упомянули версию Scalaz, которую используете, но если вы посмотрите в scalaz- Седьмая ветвь, вы найдете ПроверкаT. Это можно использовать для переноса любого F[Validation[E, A]] в ValidationT[F, E, A], где в вашем случае F = Promise. Если вы измените f и g, чтобы вернуть ValidationT, вы можете оставить свой код как

for {
  x ← f(a)
  y ← g(b)
} yield y

В результате вы получите ValidationT[Promise, E, B].

person purefn    schedule 10.06.2012
comment
Ммм, посмотри, что я ищу !! Итак, сначала я попробую с монадным преобразователем сам, по двум причинам, потому что это единственный способ научиться, а во-вторых, потому что я работаю в ветке 6.0.4. А потом, наверное, перейду на 7-й, и рефакторинг для Валидатора... В любом случае, я вернусь сюда, чтобы рассказать больше. Еще раз спасибо - person Andy Petrella; 10.06.2012
comment
В порядке. Отличный док. Я заглянул в ValidationT, его метод flatMap действительно то, что мне нужно (и, как я догадываюсь, он такой же, как мой /~~>). Но чтобы использовать его напрямую, Promise нужен экземпляр TypeClass Monad, которого нет. Поэтому, когда выйдет ScalaZ, я реализую его, чтобы иметь возможность использовать ValidationT напрямую. - person Andy Petrella; 10.06.2012