2014-10-16 81 views
7

的我开始了这样的事情:Map和Reduce /折叠HList scalaz.Validation

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(...)时,所有Kleisli将被“减少”为Validation[String, A]

+1

你一定需要将'Poly1'定义为一个对象(针对与稳定标识符有关的各种古怪的Scala相关原因)。这看起来很像遍历,而无形contrib的“遍历”可以让你一步完成'reduce(_ | @ | _)'的'toValidationNel'和(道义等价物)。 – 2014-10-16 17:45:36

+1

另请参阅我的隐约相关的博客文章[这里](http://meta.plasm.us/posts/2013/06/05/applicative-validation-syntax/)。 – 2014-10-16 17:45:57

回答

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) 

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

+0

你能详细解释一下为什么'遍历'与普通的'map'不同,为什么它的工作原理和'map'不是,以及它为什么不是(无)Shapeless的一部分? – 2014-10-17 10:18:26

+2

@TravisBrown为什么我们需要'.productElements'和'.map(_。tupled)'?不应该无形的对元组的“Generic”支持允许我们直接“遍历”元组? @ErikAllik'traverse'就像一个'map',积累'Applicative'效果。如果我们使用普通的'map',我们会得到'(ValidationNel [String,String],ValidationNel [String,String])'; '遍历'也会对“ValidationNel”效果进行“序列化”,所以我们为整个元组设置一个“ValidationNel”。 “遍历”取决于scalaz,所以它不能成为主要无形体的一部分,因为它们不想引入这种依赖关系。 – lmm 2014-10-17 10:25:06

+0

@lmm:完美,谢谢! – 2014-10-17 10:38:35