2015-12-22 5 views
0

Мне нужен комбинатор для преобразования List[ValidationNel[A, Option[B]]] в ValidationNel[A, List[B]], чтобы скорректировать валидацию и, в случае успеха, сгладить это дерьмо.Как преобразовать Список [ValidationNel [A, Option [B]]]

Вот код:

def sequenceAndFlatten[A,B](valid: List[ValidationNel[A, Option[B]]]) : ValidationNel[A, List[B]] = 
    valid.sequenceU.map(_.flatten) 

Есть ли лучший способ?

ответ

1

Мы можем использовать traverseM:

  • Цитирование документации traverseM (join это имя Scalaz для flatten):

    версия траверсы, где последующее монадические присоединиться применяются к внутреннему результат.

  • sequence такое же, как traverse(identity).

Так traverseM(identity) должен быть похож на sequence.map(_.flatten), но flatten использует неявное преобразование из Option в List в то время как Scalaz только придавить монады одного и того же типа, так что нам нужно преобразовать Option в List явно: traverseM(_.map(_.toList)).

Проблема с использованием traverseM заключается в том, что мы не можем использовать Unapply, как с вашим sequenceU. Нам нужно будет предоставить компилятору некоторую помощь и указать параметры типа (используя тип лямбда).

import scalaz.ValidationNel 
import scalaz.std.list._ 
import scalaz.syntax.traverse._ 

def seqFlat[A,B](valid: List[ValidationNel[A, Option[B]]]): ValidationNel[A, List[B]] = 
    valid.traverseM[({type l[x] = ValidationNel[A, x]})#l, B](_.map(_.toList)) 

Что дает тот же результат, как ваш sequenceAndFlatten:

import scalaz.std.option._ 
import scalaz.syntax.std.option._ 
import scalaz.syntax.validation._ 

val validations = 
    List(1.some.successNel[String], 2.some.successNel, none[Int].successNel) 

sequenceAndFlatten(validations) 
// scalaz.ValidationNel[String,List[Int]] = Success(List(1, 2)) 

seqFlat(validations) 
// scalaz.ValidationNel[String,List[Int]] = Success(List(1, 2))