有人知道,如何在不创建的情况下正确打印案例类类型层次结构吗?

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

我遇到了一个问题。我想为 Scala 中的任何类型制作一台打印机。 例如我有一个案例类

  class AAA(i: Int, s: String, o: Option[Int], bbb: BBB)
  class BBB(l: List[Int])
def explainType[T]: String

我需要一个函数,它可以采用此类的类型并返回类似的字符串

AAA(i: Int, s: String, o: Option(if possible with inner type), bbb:(l: List(if possible with inner type))

我不太关心格式,它需要不言自明。如果可以将其打印为 json,我会喜欢的。 如果你知道任何现有的图书馆,请告诉我。

提前致谢。

scala logging reflection scala-macros
2个回答
3
投票

如果你的类是 case 类,你可以使用

Shapeless
Circe 实现类型类 Explain

// I'm using Shapeless but still need this macro :)

import scala.language.experimental.macros
import scala.reflect.macros.whitebox // libraryDependencies += scalaOrganization.value % "scala-reflect" % scalaVersion.value

trait ToName[A] {
  type Out <: String with Singleton
}
object ToName {
  type Aux[A, Out0 <: String with Singleton] = ToName[A] { type Out = Out0 }

  implicit def mkToName[A, Out <: String with Singleton]: Aux[A, Out] = macro mkToNameImpl[A]

  def mkToNameImpl[A: c.WeakTypeTag](c: whitebox.Context): c.Tree = {
    import c.universe._
    val A = weakTypeOf[A]
    val toNameTpe = weakTypeOf[ToName[A]]
    q"""
      new $toNameTpe {
        type Out = ${A.typeSymbol.name.toString}
      }
    """
  }
}
// in a different subproject

import io.circe.{Json, JsonObject} // libraryDependencies += "io.circe" %% "circe-core" % "0.14.5"
import shapeless.{::, HList, HNil, LabelledGeneric, Typeable, Witness} // libraryDependencies += "com.chuusai" %% "shapeless" % "2.3.10"
import shapeless.labelled.{FieldType, field}
import shapeless.tag.@@

trait ToNameSymbol[A] {
  type Out <: Symbol
}
object ToNameSymbol {
  type Aux[A, Out0 <: Symbol] = ToNameSymbol[A] {type Out = Out0}

  implicit def mkToNameSymbol[A](implicit toName: ToName[A]): Aux[A, Symbol @@ toName.Out] = null
}

trait LabelledGenericWithName[T <: Product] {
  type Repr <: FieldType[_, _ <: HList]
  def to(t: T): Repr
  def from(r: Repr): T
}
object LabelledGenericWithName {
  type Aux[T <: Product, Repr0 <: FieldType[_, _ <: HList]] = LabelledGenericWithName[T] {type Repr = Repr0}
  def instance[T <: Product, Repr0 <: FieldType[_, _ <: HList]](f: T => Repr0, g: Repr0 => T): Aux[T, Repr0] = new LabelledGenericWithName[T] {
    override type Repr = Repr0
    override def to(t: T): Repr = f(t)
    override def from(r: Repr): T = g(r)
  }

  implicit def mkLabelledGenericWithName[T <: Product, L <: HList](implicit
    labelledGeneric: LabelledGeneric.Aux[T, L],
    toNameSymbol: ToNameSymbol[T]
  ): Aux[T, FieldType[toNameSymbol.Out, L]] = instance(
    t => field[toNameSymbol.Out](labelledGeneric.to(t)),
    t => labelledGeneric.from(t)
  )
}

trait DeepLabelledGeneric[T <: Product] {
  type Repr <: FieldType[_, _ <: HList]
  def to(t: T): Repr
  def from(r: Repr): T
}
object DeepLabelledGeneric {
  type Aux[T <: Product, Repr0 <: FieldType[_, _ <: HList]] = DeepLabelledGeneric[T] {type Repr = Repr0}
  def instance[T <: Product, Repr0 <: FieldType[_, _ <: HList]](f: T => Repr0, g: Repr0 => T): Aux[T, Repr0] = new DeepLabelledGeneric[T] {
    override type Repr = Repr0
    override def to(t: T): Repr = f(t)
    override def from(r: Repr): T = g(r)
  }

  implicit def mkDeepLabelledGeneric[A <: Product, K <: Symbol, L <: HList, L1 <: HList](implicit
    labelledGenericWithName: LabelledGenericWithName.Aux[A, FieldType[K, L]],
    hListDeepLabelledGeneric: HListDeepLabelledGeneric.Aux[L, L1]
  ): Aux[A, FieldType[K, L1]] =
    instance(
      a => field[K](hListDeepLabelledGeneric.to(labelledGenericWithName.to(a))),
      l1 => labelledGenericWithName.from(field[K](hListDeepLabelledGeneric.from(l1)) : FieldType[K, L])
    )
}

trait HListDeepLabelledGeneric[T <: HList] {
  type Repr <: HList
  def to(t: T): Repr
  def from(r: Repr): T
}
trait LowPriorityHListDeepLabelledGeneric {
  type Aux[T <: HList, Repr0 <: HList] = HListDeepLabelledGeneric[T] {type Repr = Repr0}
  def instance[T <: HList, Repr0 <: HList](f: T => Repr0, g: Repr0 => T): Aux[T, Repr0] = new HListDeepLabelledGeneric[T] {
    override type Repr = Repr0
    override def to(t: T): Repr = f(t)
    override def from(r: Repr): T = g(r)
  }

  implicit def headNotCaseClass[H, T <: HList, T_hListDeepLGen <: HList](implicit
    tailHListDeepLabelledGeneric: HListDeepLabelledGeneric.Aux[T, T_hListDeepLGen]
  ): Aux[H :: T, H :: T_hListDeepLGen] = instance({
    case h :: t => h :: tailHListDeepLabelledGeneric.to(t)
  }, {
    case h :: t => h :: tailHListDeepLabelledGeneric.from(t)
  })
}
object HListDeepLabelledGeneric extends LowPriorityHListDeepLabelledGeneric {
  implicit val hNil: Aux[HNil, HNil] = instance(identity, identity)

  implicit def headCaseClass[K <: Symbol, H <: Product, T <: HList, H_deepLGen <: FieldType[_, _ <: HList], T_hListDeepLGen <: HList](implicit
    headDeepLabelledGeneric: DeepLabelledGeneric.Aux[H, H_deepLGen],
    tailHListDeepLabelledGeneric: HListDeepLabelledGeneric.Aux[T, T_hListDeepLGen]
  ): Aux[FieldType[K, H] :: T, FieldType[K, H_deepLGen] :: T_hListDeepLGen] = instance({
    case h :: t => field[K](headDeepLabelledGeneric.to(h)) :: tailHListDeepLabelledGeneric.to(t)
  }, {
    case h :: t => field[K](headDeepLabelledGeneric.from(h)) :: tailHListDeepLabelledGeneric.from(t)
  })
}

trait Explain[T <: Product] {
  def apply(): JsonObject
}
object Explain {
  implicit def mkExplain[T <: Product, K <: Symbol, L <: HList](implicit
    deepLabelledGeneric: DeepLabelledGeneric.Aux[T, FieldType[K, L]],
    hListExplain: HListExplain[L],
    witness: Witness.Aux[K]
  ): Explain[T] = () => JsonObject(witness.value.name -> Json.fromJsonObject(hListExplain()))
}

trait HListExplain[L <: HList] {
  def apply(): JsonObject
}
object HListExplain {
  implicit val hNil: HListExplain[HNil] = () => JsonObject()

  implicit def headNotHList[K <: Symbol, H, T <: HList](implicit
    tailHListExplain: HListExplain[T],
    witness: Witness.Aux[K],
    typeable: Typeable[H]
  ): HListExplain[FieldType[K, H] :: T] =
    () => (witness.value.name -> Json.fromString(typeable.describe)) +: tailHListExplain()

  implicit def headHList[K <: Symbol, K1 <: Symbol, H <: HList, T <: HList](implicit
    headHListExplain: HListExplain[H],
    tailHListExplain: HListExplain[T],
    witness: Witness.Aux[K],
    witness1: Witness.Aux[K1],
  ): HListExplain[FieldType[K, FieldType[K1, H]] :: T] =
    () => (witness.value.name -> Json.obj(witness1.value.name -> Json.fromJsonObject(headHListExplain()))) +: tailHListExplain()
}

def explainType[T <: Product](implicit explain: Explain[T]): Json = Json.fromJsonObject(explain())

case class AAA(i: Int, s: String, o: Option[Int], bbb: BBB)
case class BBB(l: List[Int])

explainType[AAA]
//{
//  "AAA" : {
//    "i" : "Int",
//    "s" : "String",
//    "o" : "Option[Int]",
//    "bbb" : {
//      "BBB" : {
//        "l" : "List[Int]"
//      }
//    }
//  }
//}

Shapeless - 如何为 Coproduct 推导 LabelledGeneric (

ToName
)

仅使用类型导出嵌套的无形透镜 (

DeepGeneric
)

获取指向另一个案例类的案例类定义 (

DeepLabelledGeneric
)

Scala 案例类和递归反射 (

DeepGeneric
)

使用 shapeless 获取案例类字段的名称和类型

如何从 Scala 中的泛型类型获取字段名称和字段类型?

如何在编译时使用 shapeless 获取类名作为字符串文字?


如果类不一定是案例类,您可以使用

macros
实现类型类 Explain 等。也就是说,您可以将标准的
shapeless.Generic
LabelledGeneric
DefaultSymbolicLabelling
替换为以下不仅适用于案例类的实现。另外,与上面的实现相比,我将上限
<: Product
替换为类型类(上下文绑定)
IsCaseClassLike
。否则,如果我只是删除
<: Product
并且不添加任何约束,那么编译器将开始寻找标准类的通用表示形式,例如
Int
String

import shapeless.{Annotation, DepFn0, HList, Refute}
import scala.annotation.StaticAnnotation
import scala.language.experimental.macros
import scala.reflect.macros.whitebox

trait ToName[A] {
  type Out <: String with Singleton
}
object ToName {
  type Aux[A, Out0 <: String with Singleton] = ToName[A] { type Out = Out0 }

  implicit def mkToName[A, Out <: String with Singleton]: Aux[A, Out] = macro mkToNameImpl[A]

  def mkToNameImpl[A: c.WeakTypeTag](c: whitebox.Context): c.Tree = {
    import c.universe._
    val A = weakTypeOf[A]
    val toNameTpe = weakTypeOf[ToName[A]]
    q"""
      new $toNameTpe {
        type Out = ${A.typeSymbol.name.toString}
      }
    """
  }
}

// class data extends StaticAnnotation

trait IsCaseClassLike[T]
object IsCaseClassLike {
   // nothing should be converted to HList except classes annotated with @data
// implicit def mkIsCaseClassLike[T](implicit ev: Annotation[data, T]): IsCaseClassLike[T] = null

  // everything should be converted to HList except standard classes
  implicit def mkIsCaseClassLike[T](implicit ev: Refute[IsStandard[T]]): IsCaseClassLike[T] = null
}

trait IsStandard[T]
object IsStandard {
  implicit def mkIsStandard[T]: IsStandard[T] = macro mkIsStandardImpl[T]

  def mkIsStandardImpl[T: c.WeakTypeTag](c: whitebox.Context): c.Tree = {
    import c.universe._
    val symbol = symbolOf[T]
    if (Seq("scala.", "java.", "shapeless.").exists(symbol.fullName.startsWith)) q"null"
    else c.abort(c.enclosingPosition, s"$symbol is not standard class")
  }
//    implicit val int: IsStandard[Int] = null
//    implicit val str: IsStandard[String] = null
//    implicit def opt[A]: IsStandard[Option[A]] = null
//    implicit def list[A]: IsStandard[List[A]] = null
//    implicit def hlist[L <: HList]: IsStandard[L] = null
}

trait Generic[T] {
  type Repr <: HList
  def to(t: T): Repr
  def from(r: Repr): T
}
object Generic {
  type Aux[T, Repr0 <: HList] = Generic[T] {type Repr = Repr0}
  def instance[T, R <: HList](f: T => R, g: R => T): Aux[T, R] = new Generic[T] {
    override type Repr = R
    override def to(t: T): Repr = f(t)
    override def from(r: Repr): T = g(r)
  }

  implicit def mkGeneric[T, R <: HList](implicit
    ev: IsCaseClassLike[T]
  ): Aux[T, R] = macro mkGenericImpl[T]

  def mkGenericImpl[T : c.WeakTypeTag](c: whitebox.Context)(ev: c.Tree): c.Tree = {
    import c.universe._

    val T = weakTypeOf[T]
    val genTpe = weakTypeOf[Generic[T]]
    val fieldTypes = T.decls.filter(s => s.isTerm && !s.isMethod).map(_.typeSignature)
    val range = 0 until fieldTypes.size
    val names = range.map(i => q"${TermName(s"arg$i")}")
    val namePatterns = range.map(i => pq"${TermName(s"arg$i")}")
    val sh = q"_root_.shapeless"
    val (reprTpe, repr) =
      fieldTypes.zip(names)
        .foldRight[(Tree, Tree)]((tq"$sh.HNil", q"$sh.HNil")) { case ((tpe, name), (accTpe, acc)) =>
          val accTpe1 = tq"$sh.::[$tpe, $accTpe]"
          (accTpe1, q"new $accTpe1($name, $acc)")
        }
    val reprPattern = namePatterns.foldRight[Tree](pq"$sh.HNil")((name, acc) => pq"$sh.::($name, $acc)")
    val reprCase = cq"$reprPattern => new $T(..$names)"

    val to = 
      if (T.companion != NoType && T.companion.decl(TermName("unapply")) != NoSymbol) {
        val classCase = cq"${T.typeSymbol.companion}(..$namePatterns) => $repr"
        q"t match { case $classCase }"
      } else q"_root_.scala.Predef.???"

    val from = q"r match { case $reprCase }"

    q"""
      new $genTpe {
        override type Repr = $reprTpe
        override def to(t: $T): Repr   = $to
        override def from(r: Repr): $T = $from
      }
    """
  }
}

trait DefaultSymbolicLabelling[T] extends DepFn0 {
  type Out <: HList
}
object DefaultSymbolicLabelling {
  type Aux[T, Out0 <: HList] = DefaultSymbolicLabelling[T] {type Out = Out0}

  implicit def mkDefaultSymbolicLabelling[T, Out <: HList](implicit
    ev: IsCaseClassLike[T]
  ): Aux[T, Out] = macro mkDefaultSymbolicLabellingImpl[T]

  def mkDefaultSymbolicLabellingImpl[T: c.WeakTypeTag](c: whitebox.Context)(ev: c.Tree): c.Tree = {
    import c.universe._
    val T = weakTypeOf[T]
    val dslTpe = weakTypeOf[DefaultSymbolicLabelling[T]]
    val fieldNames = T.decls.filter(s => s.isTerm && !s.isMethod).map(_.name.toString.stripSuffix(" "))
    val sh = q"_root_.shapeless"
    val Sym = q"_root_.scala.Symbol"
    val SymT = tq"_root_.scala.Symbol"
    val (outTpe, out) =
      fieldNames.foldRight[(Tree, Tree)]((tq"$sh.HNil", q"$sh.HNil")) { case (name, (accTpe, acc)) =>
        val accTpe1 = tq"$sh.::[$sh.tag.@@[$SymT, $name], $accTpe]"

        (
          accTpe1,
          q"""
            new $accTpe1(
              $sh.tag.apply[$name].apply[$SymT]($Sym.apply($name)),
              $acc
            )
          """
        )
      }

    q"""
      new $dslTpe {
        override type Out = $outTpe
        override def apply(): Out = $out
      }
    """
  }
}
import io.circe.{Json, JsonObject}
import shapeless.{::, HList, HNil, Typeable, Witness, Unpack2}
import shapeless.labelled.{FieldType, KeyTag, field}
import shapeless.ops.hlist.ZipWithKeys
import shapeless.tag.@@

trait LabelledGeneric[T] {
  type Repr <: HList
  def to(t: T): Repr
  def from(r: Repr): T
}
object LabelledGeneric {
  type Aux[T, Repr0 <: HList] = LabelledGeneric[T] {type Repr = Repr0}
  def instance[T, Repr0 <: HList](f: T => Repr0, g: Repr0 => T): Aux[T, Repr0] = new LabelledGeneric[T] {
    override type Repr = Repr0
    override def to(t: T): Repr = f(t)
    override def from(r: Repr): T = g(r)
  }

  implicit def mkLabelledGeneric[T, K <: HList, V <: HList, R <: HList](implicit
    ev0: IsCaseClassLike[T],
    lab: DefaultSymbolicLabelling.Aux[T, K],
    gen: Generic.Aux[T, V],
    zip: ZipWithKeys.Aux[K, V, R],
    ev: R <:< V
  ): Aux[T, R] = instance(t => zip(gen.to(t)), gen.from(_))
}

trait ToNameSymbol[A] {
  type Out <: Symbol
}
object ToNameSymbol {
  type Aux[A, Out0 <: Symbol] = ToNameSymbol[A] {type Out = Out0}

  implicit def mkToNameSymbol[A](implicit
    toName: ToName[A]
  ): Aux[A, Symbol @@ toName.Out] = null
}

trait LabelledGenericWithName[T] {
  type Repr <: FieldType[_, _ <: HList]
  def to(t: T): Repr
  def from(r: Repr): T
}
object LabelledGenericWithName {
  type Aux[T, Repr0 <: FieldType[_, _ <: HList]] = LabelledGenericWithName[T] {type Repr = Repr0}
  def instance[T, Repr0 <: FieldType[_, _ <: HList]](f: T => Repr0, g: Repr0 => T): Aux[T, Repr0] = new LabelledGenericWithName[T] {
    override type Repr = Repr0
    override def to(t: T): Repr = f(t)
    override def from(r: Repr): T = g(r)
  }

  implicit def mkLabelledGenericWithName[T, L <: HList](implicit
    ev: IsCaseClassLike[T],
    labelledGeneric: LabelledGeneric.Aux[T, L],
    toNameSymbol: ToNameSymbol[T]
  ): Aux[T, FieldType[toNameSymbol.Out, L]] =
    instance(
      t => field[toNameSymbol.Out](labelledGeneric.to(t)),
      t => labelledGeneric.from(t)
    )
}

trait DeepLabelledGeneric[T] {
  type Repr <: FieldType[_, _ <: HList]
  def to(t: T): Repr
  def from(r: Repr): T
}
object DeepLabelledGeneric {
  type Aux[T, Repr0 <: FieldType[_, _ <: HList]] = DeepLabelledGeneric[T] {type Repr = Repr0}
  def instance[T, Repr0 <: FieldType[_, _ <: HList]](f: T => Repr0, g: Repr0 => T): Aux[T, Repr0] = new DeepLabelledGeneric[T] {
    override type Repr = Repr0
    override def to(t: T): Repr = f(t)
    override def from(r: Repr): T = g(r)
  }

  implicit def mkDeepLabelledGeneric[A, K <: Symbol, L <: HList, L1 <: HList](implicit
    ev: IsCaseClassLike[A],
    labelledGenericWithName: LabelledGenericWithName.Aux[A, FieldType[K, L]],
    hListDeepLabelledGeneric: HListDeepLabelledGeneric.Aux[L, L1]
  ): Aux[A, FieldType[K, L1]] =
    instance(
      a => field[K](hListDeepLabelledGeneric.to(labelledGenericWithName.to(a))),
      l1 => labelledGenericWithName.from(field[K](hListDeepLabelledGeneric.from(l1)) : FieldType[K, L])
    )
}

trait HListDeepLabelledGeneric[T <: HList] {
  type Repr <: HList
  def to(t: T): Repr
  def from(r: Repr): T
}
trait LowPriorityHListDeepLabelledGeneric {
  type Aux[T <: HList, Repr0 <: HList] = HListDeepLabelledGeneric[T] {type Repr = Repr0}
  def instance[T <: HList, Repr0 <: HList](f: T => Repr0, g: Repr0 => T): Aux[T, Repr0] = new HListDeepLabelledGeneric[T] {
    override type Repr = Repr0
    override def to(t: T): Repr = f(t)
    override def from(r: Repr): T = g(r)
  }

  implicit def headNotCaseClass[H, T <: HList, T_hListDeepLGen <: HList](implicit
    tailHListDeepLabelledGeneric: HListDeepLabelledGeneric.Aux[T, T_hListDeepLGen]
  ): Aux[H :: T, H :: T_hListDeepLGen] = instance({
    case h :: t => h :: tailHListDeepLabelledGeneric.to(t)
  }, {
    case h :: t => h :: tailHListDeepLabelledGeneric.from(t)
  })
}
object HListDeepLabelledGeneric extends LowPriorityHListDeepLabelledGeneric {
  implicit val hNil: Aux[HNil, HNil] = instance(identity, identity)

  implicit def headCaseClass[K <: Symbol, H, T <: HList, H_deepLGen <: FieldType[_, _ <: HList], T_hListDeepLGen <: HList](implicit
    ev: IsCaseClassLike[H],
    headDeepLabelledGeneric: DeepLabelledGeneric.Aux[H, H_deepLGen],
    tailHListDeepLabelledGeneric: HListDeepLabelledGeneric.Aux[T, T_hListDeepLGen]
  ): Aux[FieldType[K, H] :: T, FieldType[K, H_deepLGen] :: T_hListDeepLGen] = instance({
    case h :: t => field[K](headDeepLabelledGeneric.to(h)) :: tailHListDeepLabelledGeneric.to(t)
  }, {
    case h :: t => field[K](headDeepLabelledGeneric.from(h)) :: tailHListDeepLabelledGeneric.from(t)
  })
}

trait Explain[T] {
  def apply(): JsonObject
}
object Explain {
  implicit def mkExplain[T, /*FT <: FieldType[_, _ <: HList],*/ K <: Symbol, L <: HList](implicit
    ev: IsCaseClassLike[T],
      // I was afraid that .Aux[T, FieldType[K, L]] will be over-constrained implicit, see (*)
    deepLabelledGeneric: DeepLabelledGeneric.Aux[T, FieldType[K, L]], //DeepLabelledGeneric.Aux[T, FT],
    // ev1: Unpack2[FT, FieldType, K, L], // FT <:< FieldType[K, L], // FT =:= FieldType[K, L], // FT <:< KeyTag[K, L],
    hListExplain: HListExplain[L],
    witness: Witness.Aux[K]
  ): Explain[T] = () => JsonObject(witness.value.name -> Json.fromJsonObject(hListExplain()))
}

trait HListExplain[L <: HList] {
  def apply(): JsonObject
}
object HListExplain {
  implicit val hNil: HListExplain[HNil] = () => JsonObject()

  implicit def headNotHList[K <: Symbol, H, T <: HList](implicit
    tailHListExplain: HListExplain[T],
    witness: Witness.Aux[K],
    typeable: Typeable[H]
  ): HListExplain[FieldType[K, H] :: T] =
    () => (witness.value.name -> Json.fromString(typeable.describe)) +: tailHListExplain()

  implicit def headHList[K <: Symbol, K1 <: Symbol, H <: HList, T <: HList](implicit
    headHListExplain: HListExplain[H],
    tailHListExplain: HListExplain[T],
    witness: Witness.Aux[K],
    witness1: Witness.Aux[K1],
  ): HListExplain[FieldType[K, FieldType[K1, H]] :: T] =
    () => (witness.value.name -> Json.obj(witness1.value.name -> Json.fromJsonObject(headHListExplain()))) +: tailHListExplain()
}

def explainType[T](implicit explain: Explain[T]): Json = Json.fromJsonObject(explain())

/*@data*/
/*case*/ class AAA(val i: Int, val s: String, o: Option[Int], bbb: BBB)

/*@data*/
/*case*/ class BBB(l: List[Int])

explainType[AAA]
//{
//  "AAA" : {
//    "i" : "Int",
//    "s" : "String",
//    "o" : "Option[Int]",
//    "bbb" : {
//      "BBB" : {
//        "l" : "List[Int]"
//      }
//    }
//  }
//}

(*) 为什么这个隐式解析失败了?HList foldLeft 元组为零


0
投票

如果您使用的是 Scala 3(又名 dotty),这里有一个更简单的解决方案,无需依赖任何第三方库。

用例:

λ scalac Macros.scala

λ scala -cp .
Welcome to Scala 3.2.0-RC2.
Type in expressions for evaluation. Or try :help.

scala> import Macros.explainType

scala> explainType[(Int, Double, String)]
val res0: String = (Int, Double, String)

scala> explainType[List[Option[Int]]]
val res1: String = List[Option[Int]]

scala> case class BBB(l: List[Int])
// defined case class BBB

scala> case class AAA(i: Int, s: String, o: Option[Int], bbb: BBB)
// defined case class AAA

scala> explainType[BBB]
val res2: String = BBB(l :List[Int])

scala> explainType[AAA]
val res3: String = AAA(i :Int, s :String, o :Option[Int], bbb :BBB(l :List[Int]))

这里是实现:

// Save it as Macros.scala
object Macros:

    import scala.compiletime.*
    import scala.deriving.Mirror
    import scala.quoted.*

    type Head[T] = T match
        case h *: _ => h

    type Tail[T] = T match
        case _ *: t => t

    def simpleNameImp[A](using Type[A], Quotes): Expr[String] =
        Expr(Type.show[A].replaceAll("""(scala|java)\.([$\w]+\.)*""", ""))

    inline def simpleName[A]: String = ${ simpleNameImp[A] }

    def isInnerTypeImp[A](using Type[A], Quotes): Expr[Boolean] =
        Expr(Type.show[A].startsWith("scala.") || Type.show[A].startsWith("java."))

    inline def isInnerType[A]: Boolean = ${ isInnerTypeImp[A] }

    inline def tupleName[T]: List[String] = inline erasedValue[T] match
        case EmptyTuple => Nil
        case _          => explainType[Head[T]] :: tupleName[Tail[T]]

    inline def explainType[A]: String = inline erasedValue[A] match
        case _: Tuple            => tupleName[A].mkString("(", ", ", ")")
        case _ if isInnerType[A] => simpleName[A]
        case _                   => summonFrom {
            case m: Mirror.Of[A] =>
                val name   = constValue[m.MirroredLabel]
                val labels = constValueTuple[m.MirroredElemLabels]
                val types  = tupleName[m.MirroredElemTypes]
                name + (labels.toArray zip types).map { (lab, typ) => s"$lab :$typ" }.mkString("(", ", ", ")")
            case _               => simpleName[A]
        }
© www.soinside.com 2019 - 2024. All rights reserved.