【问题标题】:Map and reduce/fold over HList of scalaz.Validation在 scalaz.Validation 的 HList 上映射和减少/折叠
【发布时间】:2014-10-16 17:09:14
【问题描述】:

我是这样开始的:

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

附言实际上,我的验证被包裹在Kleisli[Va, A, B] 中,这样我就可以使用>=> 编写单独的验证步骤,但这似乎与问题正交,因为当到达.map(...) 时,所有Kleislis 都将是“减少”为Validation[String, A]

【问题讨论】:

  • 您肯定需要将Poly1 定义为一个对象(出于各种与稳定标识符有关的古怪的Scala 相关原因)。这看起来很像遍历,而 shapeless-contrib 的 traverse 可以让您一步完成 toValidationNelreduce(_ |@| _)(道德等价物)。
  • 另见我模糊相关的博文here

标签: scala validation scalaz shapeless scalaz7


【解决方案1】:

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

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

【讨论】:

  • 您能否详细说明traverse 与普通map 不同的原因和方式,为什么它有效而map 无效,以及为什么它不是(还)一部分无形者?
  • @TravisBrown 为什么我们需要.productElements.map(_.tupled)?不应该 shapeless 的 Generic 对元组的支持允许我们直接 traverse 元组吗? @ErikAllik traverse 就像一个 map 累积 Applicative 效果。如果我们使用普通的map,我们会得到(ValidationNel[String, String], ValidationNel[String, String])traverse 也“序列化”ValidationNel 效果,所以我们有一个 ValidationNel 用于整个元组。 traverse 依赖于 scalaz,因此它不能成为主要 Shapeless 的一部分,因为他们不想引入这种依赖关系。
  • Travis:任何关于为什么 ApplicativeBuilder 糟糕的指针(例如,与普通的 ValidationNel 相比)将不胜感激!
  • @Erik:ApplicativeBuilder 的问题在于它只是一种语法黑客——它没有任何意义。它本质上是一个等待函数的参数列表,它可以提升到应用函子并应用于自身。
  • 所以这很糟糕,因为语法黑客一般都很糟糕,或者只是这个特定的黑客很糟糕?还是只是不需要?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-21
  • 2018-10-29
  • 2023-03-04
  • 1970-01-01
  • 2020-01-01
  • 1970-01-01
相关资源
最近更新 更多