简体   繁体   中英

Scala: How to transform an HList of ValidatedNel into a ValidatedNel of HList

I've been using cats.data.Validated successfully to solve the problem below, but have come into a problem using my existing solution for a case class with more than 22 members (because the constructor cannot be made into a Function ).

Here is my goal: generate a bunch of ValidatedNel[E, T] , sequence them into a ValidatedNel[E, (T1, T2, ...)] , then mapN(DAOClass) (where DAOClass is a case class with the specified arguments). This works for fewer than 22 arguments, but fails with more because of two problems:

  1. (T1, T2, ...) cannot have more than 22 components
  2. DAOClass.apply cannot be transformed into a Function

So I am looking into using shapeless.HList to handle part 1 and having problems. I should be able to use Generic[DAOClass] to satisfactorily handle part 2 when I get to it, or if that doesn't work, use extensible records with a bit more boilerplate.

Here is some small example code (not with 22 components):

package example

import cats.syntax.validated._
import cats.data.ValidatedNel
import cats.sequence._
import shapeless._

case class DAOClass(a: Int, b: Int)

object DAOClass {

  def generate: ValidatedNel[String, DAOClass] = {

    val hlist: ValidatedNel[String, Int] :: ValidatedNel[String, Int] :: HNil =
      1.validNel :: 2.validNel :: HNil

    val hlistSequence: ValidatedNel[String, Int :: Int :: HNil] = hlist.sequence

    hlistSequence.map(Generic[DAOClass].from)

  }

}

This uses the kittens library to sequence the HList .

Unfortunately, this gives me a compile error:

[error] ...src/main/scala/example/DAOClass.scala:17:73: cannot construct sequencer, make sure that every item of your hlist shapeless.:: [cats.data.ValidatedNel[String,Int],shapeless.::[cats.data.ValidatedNel[String,Int],shapeless.HNil]] is an Apply
[error]     val hlistSequence: ValidatedNel[String, ::[Int, ::[Int, HNil]]] = hlist.sequence
[error]                                                                             ^

I have extracted this into a test project; here's my build.sbt :

lazy val root = (project in file(".")).
  settings(
    inThisBuild(List(
      organization := "com.example",
      scalaVersion := "2.12.6",
      version      := "0.1.0-SNAPSHOT"
    )),
    name := "shapeless-validation",
    resolvers ++= Seq(
      Resolver.sonatypeRepo("releases")
    ),
    libraryDependencies ++= Seq(
      "com.chuusai"   %% "shapeless" % "2.3.3",
      "org.scalatest" %% "scalatest" % "3.0.5" % "test",
      "org.typelevel" %% "cats-core" % "1.1.0",
      "org.typelevel" %% "kittens"   % "1.1.0"
    )
  )

What am I missing? Do I need to import more implicits somewhere? Is there a better way to do this?

You forgot to add

scalacOptions += "-Ypartial-unification"

to build.sbt . For normal work with cats this is usually mandatory.

Now

hlistSequence.map(Generic[DAOClass].from)

produces a ValidatedNel[String, DAOClass] :

println(DAOClass.generate) // Valid(DAOClass(1,2))

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM