获取 case class 参数类型作为 HList

Get case class parameter types as a HList

我正在尝试使用 shapeless

生成案例 class 的实例

这适用于生成 Foo

的实例
case class Foo(x: Int, y: String)

class Context {
  val random = new Random()
}

def genInt(context: Context): Int = {
  context.random.nextInt()
}

def genString(context: Context): String = {
  context.random.nextString(16)
}

object ClassesToGenerators extends Poly1 {
  implicit def caseInt = at[Class[Int]](_ => genInt(_))
  implicit def caseString = at[Class[String]](_ => genString(_))
}


val gen = Generic[Foo]
val context = new Context()
val classes = classOf[Int] :: classOf[String] :: HNil // can't figure out how to get this hlist programmatically
val generators = classes.map(ClassesToGenerators)

gen.from(generators.zipApply(generators.mapConst(context)))

但是,我的目标是写一些可重用的东西,比如

def newInstance[T] -> T:
  ???

它可以生成任何 case 的实例 classes 只接受 int 和 string 参数。

如代码片段中所述,我一直在获取案例 class 属性类型的 hlist,即想将 case class Foo(x: Int, y: String) 转换为 classOf[Int] :: classOf[String] :: HNil。解决此问题的任何其他方法也非常受欢迎,但我不是在寻找一种生成案例随机实例的通用方法 classes(因为我的用例不同,并且使用随机生成器作为示例)

恕我直言,Shapeless 更适用于当您忘记所有花哨的东西而只关注使用 HList 的简单类型类派生时,如下所示:

import shapeless.{Generic, HList, HNil, :: => :!:}
import scala.util.Random

trait Context {
  def random: Random
}
object Context {
  object implicits {
    implicit final val global: Context = new Context {
      override final val random: Random = new Random() 
    }
  }
}

trait Generator[A] {
  def generate(context: Context): A
}
object Generator {
  final def apply[A](implicit ev: Generator[A]): ev.type = ev
  
  final def generate[A](implicit ev: Generator[A], ctx: Context): A =
    ev.generate(ctx)
  
  implicit final val IntGenerator: Generator[Int] =
    new Generator[Int] {
      override def generate(context: Context): Int =
        context.random.nextInt()
    }
  
  implicit final val StringGenerator: Generator[String] =
    new Generator[String] {
      override def generate(context: Context): String =
        context.random.nextString(16)
    }
  
  implicit final def auto[P <: Product](implicit ev: GeneratorGen[P]): Generator[P] = ev
}

sealed trait GeneratorRepr[R <: HList] extends Generator[R]
object GeneratorRepr {
  implicit final val HNilGeneratorRepr: GeneratorRepr[HNil] =
    new GeneratorRepr[HNil] {
      override def generate(context: Context): HNil =
        HNil
    }
  
  implicit final def HConsGeneratorRepr[E, T <: HList](
    implicit ev: Generator[E], tail: GeneratorRepr[T]
  ): GeneratorRepr[E :!: T] =
    new GeneratorRepr[E :!: T] {
      override def generate(context: Context): E :!: T =
        ev.generate(context) :: tail.generate(context)
    }
}

sealed trait GeneratorGen[P <: Product] extends Generator[P]
object GeneratorGen {
  implicit final def instance[P <: Product, R <: HList](
    implicit gen: Generic.Aux[P, R], ev: GeneratorRepr[R]
  ): GeneratorGen[P] = new GeneratorGen[P] {
    override def generate(context: Context): P =
      gen.from(ev.generate(context))
  }
}

可以这样使用:

import Context.implicits.global

final case class Foo(x: Int, y: String)

val result = Generator.generate[Foo]
// result: Foo = Foo(-2127375055, "鞰Ϗƨ⹼沺㗝䚮Ⴍ욏ꖱꬮӝ闉믃雦峷")

可以看到代码运行 here.