映射和缩小scalaz.Lalidation的HList
我从这样的事情开始:
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寻求帮助,但似乎并没有什么人可以提供开箱即用的答案。 不过,我非常热衷于学习如何解决这个问题,既为实践也为学习目的。
PS实际上,我的验证被封装在Kleisli[Va, A, B]
这样我就可以使用>=>
来构建单个验证步骤,但似乎与问题是正交的,就像.map(...)
所有Kleisli
将被“减少”为Validation[String, A]
。
下面是使用shapeless-contrib的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")
)
接着:
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
的事情你会从降低得到|@|
。