Map and reduce/fold over HList of scalaz.Validation

丶灬走出姿态 提交于 2020-01-12 05:21:05

问题


I started out with something like this:

def nonEmpty[A] = (msg: String) => (a: Option[A]) => a.toSuccess(msg)

val postal: Option[String] = request.param("postal")
val country: Option[String] = request.param("country")

val params =
  (postal  |> nonEmpty[String]("no postal" )).toValidationNel |@|
  (country |> nonEmpty[String]("no country")).toValidationNel

params { (postal, country) => ... }

Now I thought it would be nice to reduce the boilerplate for better readability and for not having to explain to more junior team members what .toValidateNel and |@| mean. The first thought was List but then the last line would stop working and I'd have to give up some static safety. So I looked towards Shapeless:

import shapeless._; import poly._; import syntax.std.tuple._

val params = (
  postal  |> nonEmpty[String]("no postal"),
  country |> nonEmpty[String]("no country")
)

params.map(_.toValidatioNel).reduce(_ |@| _)

however, I can't even seem to get past the .map(...) bit. I've tried as per a suggestion on #scalaz:

type Va[+A] = Validation[String, A]
type VaNel[+A] = ValidationNel[String, A]

params.map(new (Va ~> VaNel) { def apply[T](x: Va[T]) = x.toValidationNel })

...to no avail.

I've asked for help on #scalaz but it doesn't seem something people just have an out of the box answer to. However, I'm really keen on learning how to solve this both for practical as well as learning purposes.

P.S. in reality my validations are wrapped inside Kleisli[Va, A, B] so that I could compose individual validation steps using >=> but that seems to be orthogonal to the issue as by the time that .map(...) is reached, all Kleislis will have been "reduced" to Validation[String, A].


回答1:


Here's what this would look like with shapeless-contrib's traverse:

import scalaz._, Scalaz._
import shapeless._, contrib.scalaz._, syntax.std.tuple._

def nonEmpty[A] = (msg: String) => (a: Option[A]) => a.toSuccess(msg)

val postal: Option[String] = Some("00000")
val country: Option[String] = Some("us")

val params = (
  postal  |> nonEmpty[String]("no postal"),
  country |> nonEmpty[String]("no country")
)

And then:

object ToVNS extends Poly1 {
  implicit def validation[T] = at[Validation[String, T]](_.toValidationNel)
}

val result = traverse(params.productElements)(ToVNS).map(_.tupled)

Now result is a ValidationNel[String, (String, String)], and you can do anything with it that you could do with the awful ApplicativeBuilder thing you'd get from reducing with |@|.



来源:https://stackoverflow.com/questions/26410100/map-and-reduce-fold-over-hlist-of-scalaz-validation

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!