Асинхронные вычисления с валидацией в 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-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 — «делать»).

Еще момент, у меня есть /~>, который стоит как картатоже, но работает на втором уровне (вместо действительного типа -- третьегоуровень)

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

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

7
задан andy petrella 10 June 2012 в 10:47
поделиться