我最开始的内容大致是这样的:
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
,但这样最后一行将停止工作,我必须放弃一些静态安全性。因此,我转向了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(_ |@| _)
然而,我甚至无法通过.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(...)
时,所有的 Kleisli
都将被“缩减”为 Validation[String, A]
,这似乎与该问题不相关。
Poly1
定义为一个对象(由于与稳定标识符有关的各种奇怪的Scala相关原因)。这看起来很像一个遍历,而shapeless-contrib的traverse
可以让你在一步中执行toValidationNel
和(道义上等价于)reduce(_ |@| _)
。 - Travis Brown