Scala 3 编译时镜像:当我没有该类型的实例时如何获取字段名称

问题描述 投票:0回答:1

我定义这个是为了序列化任何字段为标量的案例类:

import scala.compiletime.*
import scala.deriving.*

trait Scalar[T]:
  def convert(t: T): AnyRef
  val sqlType: String

given Scalar[String] with
   def convert(t: String) = t
   val sqlType = "VARCHAR"

given Scalar[Int] with
   def convert(t: Int) = Integer.valueOf(t)
   val sqlType = "INTEGER"

trait Serializable[T]:
   def serialize(v: T): Map[String, AnyRef]
   def fields(v: T): Map[String, String]

object Serializable:
    inline def summonAll[T <: Tuple]: List[Scalar[?]] =
        inline erasedValue[T] match
            case _: EmptyTuple => Nil
            case _: (t *: ts) => summonInline[Scalar[t]] :: summonAll[ts]

    inline given derived[A <: Product](using m: Mirror.ProductOf[A]): Serializable[A] =
        val scalars = summonAll[m.MirroredElemTypes]
        new Serializable[A]:
            override def serialize(v: A): Map[String, AnyRef] =
                val pro = v.asInstanceOf[Product]
                pro.productElementNames.zip(pro.productIterator).zip(scalars).map {
                    case ((name, value), scalar) => 
                        name -> scalar.asInstanceOf[Scalar[Any]].convert(value)
                }.toMap
            override def fields(v: A): Map[String, String] =
                val pro = v.asInstanceOf[Product]
                pro.productElementNames.zip(scalars).map {
                    case (name, scalar) => 
                        name -> scalar.asInstanceOf[Scalar[Any]].sqlType
                }.toMap

case class Person(name: String, age: Int) derives Serializable

它的工作原理要归功于神奇的编译时镜像。

但是我需要定义一下:

trait Serializable[T]:
   def serialize(v: T): Map[String, AnyRef]
   val fields: Map[String, String]

而不是:

   def fields(v: T): Map[String, String]

因为字段不依赖于实例,而是依赖于类类型。

问题是,当我没有案例类的实例时,我不知道如何获取字段名称。

scala reflection scala-3
1个回答
0
投票

这是我找到的解决方案,也许它不是最好的,但它有效:

#!/usr/bin/env -S scala-cli -j system
import scala.compiletime.*
import scala.deriving.*

trait Scalar[T]:
  def convert(t: T): AnyRef
  val sqlType: String

given Scalar[String] with
   def convert(t: String) = t
   val sqlType = "VARCHAR"

given Scalar[Int] with
   def convert(t: Int) = Integer.valueOf(t)
   val sqlType = "INTEGER"

trait Serializable[T]:
   def serialize(v: T): Map[String, AnyRef]
   val fields: Map[String, String]

object Serializable:
    inline def summonAll[T <: Tuple]: List[Scalar[?]] =
        inline erasedValue[T] match
            case _: EmptyTuple => Nil
            case _: (t *: ts) => summonInline[Scalar[t]] :: summonAll[ts]

    inline given derived[A <: Product](using m: Mirror.ProductOf[A]): Serializable[A] =
        val scalars = summonAll[m.MirroredElemTypes]
        new Serializable[A]:
            override def serialize(v: A): Map[String, AnyRef] =
                val pro = v.asInstanceOf[Product]
                pro.productElementNames.zip(pro.productIterator).zip(scalars).map {
                    case ((name, value), scalar) => 
                        name -> scalar.asInstanceOf[Scalar[Any]].convert(value)
                }.toMap
            override val fields: Map[String, String] =
                import scala.compiletime.constValueTuple
                val nombres = constValueTuple[m.MirroredElemLabels].productIterator
                nombres.zip(scalars).map {
                    case (name, scalar) => 
                        name.toString() -> scalar.asInstanceOf[Scalar[Any]].sqlType
                }.toMap

case class Person(name: String, age: Int) derives Serializable
val sp = summon[Serializable[Person]]
assert(sp.fields("age") == "INTEGER")
© www.soinside.com 2019 - 2024. All rights reserved.