7

我从这样的事情开始:

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 上寻求帮助,但人们似乎没有开箱即用的答案。但是,我真的很想学习如何解决这个问题,既实用又学习。

PS 实际上,我的验证被包装在里面Kleisli[Va, A, B],这样我就可以使用它来组成单独的验证步骤,>=>但这似乎与问题正交,因为到那时.map(...),所有Kleislis 都将被“减少”到Validation[String, A].

4

1 回答 1

3

这是使用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)

现在resultValidationNel[String, (String, String)],你可以用它做任何事情,你可以用它ApplicativeBuilder来减少你从减少中得到的可怕的事情|@|

于 2014-10-16T23:21:12.437 回答