7

I started out with something like this:

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) => ... }

Now I thought it would be nice to reduce the boilerplate for better readability and for not having to explain to more junior team members what .toValidateNel and |@| mean. The first thought was List but then the last line would stop working and I'd have to give up some static safety. So I looked towards 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(_ |@| _)

however, I can't even seem to get past the .map(...) bit. I've tried as per a suggestion on #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 })

...to no avail.

I've asked for help on #scalaz but it doesn't seem something people just have an out of the box answer to. However, I'm really keen on learning how to solve this both for practical as well as learning purposes.

P.S. in reality my validations are wrapped inside Kleisli[Va, A, B] so that I could compose individual validation steps using >=> but that seems to be orthogonal to the issue as by the time that .map(...) is reached, all Kleislis will have been "reduced" to Validation[String, A].

Erik Kaplun
  • 37,128
  • 15
  • 99
  • 111
  • 1
    You'll definitely need to define the `Poly1` as an object (for various wacky Scala-related reasons having to do with stable identifiers). This looks a lot like a traversal, and shapeless-contrib's `traverse` would let you do the `toValidationNel` and (the moral equivalent of) the `reduce(_ |@| _)` in one step. – Travis Brown Oct 16 '14 at 17:45
  • 1
    See also my vaguely related blog post [here](http://meta.plasm.us/posts/2013/06/05/applicative-validation-syntax/). – Travis Brown Oct 16 '14 at 17:45

1 Answers1

3

Here's what this would look like with shapeless-contrib's 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")
)

And then:

object ToVNS extends Poly1 {
  implicit def validation[T] = at[Validation[String, T]](_.toValidationNel)
}

val result = traverse(params.productElements)(ToVNS).map(_.tupled)

Now result is a ValidationNel[String, (String, String)], and you can do anything with it that you could do with the awful ApplicativeBuilder thing you'd get from reducing with |@|.

Travis Brown
  • 138,631
  • 12
  • 375
  • 680
  • could you elaborate a little bit on why and how `traverse` is different from just a regular `map`, why it works and `map` doesn't, and why it's not (yet) part of Shapeless? – Erik Kaplun Oct 17 '14 at 10:18
  • 2
    @TravisBrown Why do we need the `.productElements` and `.map(_.tupled)`? Shouldn't shapeless's `Generic` support for tuples allow us to `traverse` the tuple directly? @ErikAllik `traverse` is like a `map` that accumulates `Applicative` effects. If we were to use regular `map` we'd get a `(ValidationNel[String, String], ValidationNel[String, String])`; the `traverse` also "sequences" the `ValidationNel` effect so we have a single `ValidationNel` for the overall tuple. `traverse` depends on scalaz, so it can't be part of main Shapeless as they don't want to introduce that dependency. – lmm Oct 17 '14 at 10:25
  • Travis: any pointers to why `ApplicativeBuilder` is awful (e.g. vs just a plain `ValidationNel`) would be greatly appreciated! – Erik Kaplun Oct 17 '14 at 11:00
  • 1
    @Erik: the problem with `ApplicativeBuilder` is that it's just a syntax hack—it doesn't mean anything. It's essentially a parameter list waiting for a function that it can lift into the applicative functor and apply to itself. – Travis Brown Oct 17 '14 at 11:33
  • So it's bad because syntax hacks are bad in general, or it's just that this particular hack is bad? Or just unneeded? – Erik Kaplun Oct 17 '14 at 11:47
  • I guess "awful" is a little strong, but contrast with Haskell, where you just write `foo <$> a <*> b <*> c` (function and arguments in the usual order, no intermediate `Builder` things, etc.). – Travis Brown Oct 17 '14 at 11:54