1

これは次の質問です。

shapelessを使用してコンパイル時にケースクラスフィールドの名前を文字列/シンボルとして取得する方法は?

製品タイプを変換できる再帰コンバーターを書きたいとします。

case class Prod (
  a: Int,
  b: String
)

ただし、各ケース クラス フィールド (a、b) をキーとして使用する上記の質問とは異なり、各クラス名または型/型コンストラクター名を直接使用したいと考えています。したがって、この積の型はコンパイル時に Record になります。

"Int" ->> Int
"String" ->> String

(おそらく十分なユースケースではありませんが、アイデアは得られました)

この重要なステップの 1 つは、リフレクションを使用してコンパイル時に各クラスの名前を取得し、それらをシングルトン型またはシェイプレス ウィットネスに変換することです。この機能はすでにどこかで提供されているのだろうか?それとも、それを実現するために絶対にホワイトボックス マクロが必要ですか?

4

1 に答える 1

2

マクロを書く必要があります

import shapeless.ops.hlist.Mapper
import shapeless.{Generic, HList, Poly1, Typeable}
import scala.language.experimental.macros
import scala.reflect.macros.whitebox

trait FieldTypes[A <: Product] {
  type Out <: HList
}
object FieldTypes {
  type Aux[A <: Product, Out0 <: HList] = FieldTypes[A] { type Out = Out0 }

  implicit def mkFieldTypes[A <: Product, L <: HList](implicit
    generic: Generic.Aux[A, L],
    mapper: Mapper[typeablePoly.type, L]
  ): Aux[A, mapper.Out] = null

  object typeablePoly extends Poly1 {
    implicit def cse[A](implicit typeable: Typeable[A]): Case[A] = macro cseImpl[A]
    def cseImpl[A: c.WeakTypeTag](c: whitebox.Context)(typeable: c.Tree): c.Tree = {
      import c.universe._
      val str = c.eval(c.Expr[String](c.untypecheck(q"$typeable.describe")))
      val tpA = weakTypeOf[A]
      q"null.asInstanceOf[FieldTypes.typeablePoly.Case.Aux[$tpA, _root_.shapeless.labelled.FieldType[$str, $tpA]]]"
    }
  }
}

テスト:

import shapeless.{HNil, ::}
import shapeless.labelled.FieldType

implicitly[FieldTypes.Aux[Prod, FieldType["Int", Int] :: FieldType["String", String] :: HNil]]
于 2021-03-28T14:15:05.833 に答える