简体   繁体   English

类型 Class 推导访问默认值

[英]Type Class Derivation accessing default values

Is there a clean way to access the default values of a case class fields when performing type class derivation in Scala 3 using Mirrors?在 Scala 3 使用镜像执行类型 class 推导时,是否有一种干净的方法来访问 case class 字段的默认值? For example:例如:

case class Foo(s: String = "bar", i: Int, d: Double = Math.PI)

Mirror.Product.MirroredElemLabels will be set to ("s", "i", "d") . Mirror.Product.MirroredElemLabels将设置为("s", "i", "d") Is there anything like: (Some["bar"], None, Some[3.141592653589793]) ?有没有类似的东西: (Some["bar"], None, Some[3.141592653589793])

If not could this be achieved using Macros?如果不能,这可以使用宏来实现吗? Can I use the Mirrors and Macros simultaneously to derive a type class instance?我可以同时使用镜像和宏来派生类型为 class 的实例吗?

As Dmytro suggests, information is carried in methods <init>default$x of the class companion object. However, Quotes discourages accessing a symbol's tree in a macro: https://github.com/lampepfl/dotty/blob/main/library/src/scala/quoted/Quotes.scala#L3628 .正如 Dmytro 所建议的那样,信息在 class 伴侣 object 的方法<init>default$x中进行。但是, Quotes不鼓励在宏中访问符号的树: https://github.com/lampepfl/dotty/blob/main/library /src/scala/quoted/Quotes.scala#L3628 Symbol's tree is lost, unless program is compiled with -Yretain-trees )符号的树丢失,除非使用-Yretain-trees编译程序)

It is better to let the macro evaluate <init>default$x , rather than copy the right hand side of its definition.最好让宏评估<init>default$x ,而不是复制其定义的右侧。

One can do so by expressing terms as:可以通过将terms为:

    val terms: List[Option[Term]] =
      (1 to n).toList.map(i =>
        TypeRepr.of[T].typeSymbol
          .companionClass
          .declaredMethod(s"$$lessinit$$greater$$default$$$i")
          .headOption
          .map(Select(Ref(TypeRepr.of[T].typeSymbol.companionModule),_))
      )

You'll have to write a macro working with methods named like <init>$default$1 , <init>$default$2 , ... in companion object您必须编写一个宏,使用名为<init>$default$1 , <init>$default$2 , ... 在伴随对象中的方法

import scala.quoted.*

inline def printDefaults[T]: Unit = ${printDefaultsImpl[T]}

def printDefaultsImpl[T](using Quotes, Type[T]): Expr[Unit] = {
  import quotes.reflect.*

  (1 to 3).map(i =>
    TypeRepr.of[T].typeSymbol
      .companionClass
      .declaredMethod(s"$$lessinit$$greater$$default$$$i")
      .headOption
      .flatMap(_.tree.asInstanceOf[DefDef].rhs)
  ).foreach(println)

 '{()}
}

printDefaults[Foo]
//Some(Literal(Constant(bar)))
//None
//Some(Select(Ident(Math),PI))

Mirrors and macros can work together:镜像和宏可以一起工作:

import scala.quoted.*
import scala.deriving.*

trait Default[T] {
  type Out <: Tuple
  def defaults: Out
}

object Default {
  transparent inline given mkDefault[T](using 
    m: Mirror.ProductOf[T], 
    s: ValueOf[Tuple.Size[m.MirroredElemTypes]]
  ): Default[T] =
    new Default[T] {
      type Out = Tuple.Map[m.MirroredElemTypes, Option]
      def defaults = getDefaults[T](s.value).asInstanceOf[Out]
    }

  inline def getDefaults[T](inline s: Int): Tuple = ${getDefaultsImpl[T]('s)}

  def getDefaultsImpl[T](s: Expr[Int])(using Quotes, Type[T]): Expr[Tuple] = {
    import quotes.reflect.*

    val n = s.asTerm.underlying.asInstanceOf[Literal].constant.value.asInstanceOf[Int]

    val terms: List[Option[Term]] =
      (1 to n).toList.map(i =>
        TypeRepr.of[T].typeSymbol
          .companionClass
          .declaredMethod(s"$$lessinit$$greater$$default$$$i")
          .headOption
          .flatMap(_.tree.asInstanceOf[DefDef].rhs)
      )

    def exprOfOption[T](oet: Option[Expr[T]])(using Type[T], Quotes): Expr[Option[T]] = oet match {
      case None => Expr(None)
      case Some(et) => '{Some($et)}
    }

    val exprs: List[Option[Expr[Any]]] = terms.map(_.map(_.asExprOf[Any]))
    val exprs1: List[Expr[Option[Any]]] = exprs.map(exprOfOption)
    Expr.ofTupleFromSeq(exprs1)
  }
}

Usage:用法:

val d = summon[Default[Foo]]
summon[d.Out =:= (Option[String], Option[Int], Option[Double])] // compiles
d.defaults // (Some(bar),None,Some(3.141592653589793))

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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