获取案例类参数类型作为HLIST

发布于 2025-01-26 13:54:58 字数 1204 浏览 1 评论 0原文

我正在尝试使用无形的

这用于生成foofoo 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:
  ???

可以生成任何仅获取int和字符串参数的情况类的实例。

如代码段中提到的那样,我坚持要获得案例类属性类型的HLIST,即想将case类foo(x:int,y:string)转换为classof [ int] :: classof [string] :: hnil。解决此问题的任何其他方法也非常感谢,但我并不是寻找一种生成案例类的随机实例的通用方法(因为我的用例是不同的,并且以随机生成器为例)

I'm trying to generate instances of case class using shapeless

This works for generating instances of 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)))

However, I'm aiming to write something reusable like

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

which could generate instances of any case classes that takes only int and string parameters.

As mentioned in the code snippet, I'm stuck at getting a hlist of case class attribute types i.e. would like to convert case class Foo(x: Int, y: String) to classOf[Int] :: classOf[String] :: HNil. Any other approaches to this problem are also very appreciated but I'm not looking for a generic way of generating random instances of cases classes (as my use-case is different and used random generator just as an example)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

少女情怀诗 2025-02-02 13:54:58

当您忘记所有花哨的东西时, 无形可以更好地使用使用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, "鞰Ϗƨ⹼沺㗝䚮Ⴍ욏ꖱꬮӝ闉믃雦峷")

您可以看到运行的代码

IMHO, Shapeless is better used when you forget about all the fancy stuff and just focus on simple typeclass derivation using HList like this:

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

Which can be used like this:

import Context.implicits.global

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

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

You can see the code running here.

单调的奢华 2025-02-02 13:54:58

使用内置的无形类型类

import shapeless.ops.hlist.FillWith
import shapeless.{Generic, HList, Poly0}

val context = new Context()

object ClassesToGenerators extends Poly0 {
  implicit val caseInt = at[Int](genInt(context))
  implicit val caseString = at[String](genString(context))
}

def newInstance[A] = new PartiallyApplied[A]

class PartiallyApplied[A] {
  def apply[L <: HList]()(implicit
    generic: Generic.Aux[A, L],
    fillWith: FillWith[ClassesToGenerators.type, L]
  ): A = generic.from(fillWith())
}

newInstance[Foo]() // Foo(2018031886,⮐掐禃惌ᰧ佨妞꨸ዤࠒ훿柲籐妭蝱⻤)

Using built-in Shapeless type classes you can do

import shapeless.ops.hlist.FillWith
import shapeless.{Generic, HList, Poly0}

val context = new Context()

object ClassesToGenerators extends Poly0 {
  implicit val caseInt = at[Int](genInt(context))
  implicit val caseString = at[String](genString(context))
}

def newInstance[A] = new PartiallyApplied[A]

class PartiallyApplied[A] {
  def apply[L <: HList]()(implicit
    generic: Generic.Aux[A, L],
    fillWith: FillWith[ClassesToGenerators.type, L]
  ): A = generic.from(fillWith())
}

newInstance[Foo]() // Foo(2018031886,⮐掐禃惌ᰧ佨妞꨸ዤࠒ훿柲籐妭蝱⻤)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文