映射并缩小/折叠scalaz.Validation的HList

时间:2014-10-16 17:09:14

标签: scala validation scalaz shapeless scalaz7

我开始时有这样的事情:

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) => ... }

现在我认为减少样板以获得更好的可读性并且不必向更多的初级团队成员解释.toValidateNel|@|是什么意思。第一个想法是List,但最后一行将停止工作,我不得不放弃一些静态安全。所以我看向无形:

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

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

params.map(_.toValidatioNel).reduce(_ |@| _)
然而,我甚至无法超越.map(...)位。我按照#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 })

......无济于事。

我已经在#scalaz上寻求过帮助,但似乎人们只有一个开箱即用的答案。但是,我非常热衷于学习如何在实际和学习目的上解决这个问题。

P.S。实际上我的验证都包含在Kleisli[Va, A, B]中,因此我可以使用>=>撰写单独的验证步骤,但这似乎与问题正交,因为到达.map(...)时,所有{ {1}}已被“缩减”为Kleisli

1 个答案:

答案 0 :(得分:3)

以下是shapeless-contribtraverse

的情况
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")
)

然后:

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

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

现在result是一个ValidationNel[String, (String, String)],你可以用它来做任何事情,你可以用ApplicativeBuilder来减少你|@|