Bagaimana cara menggunakan Shapeless dalam Kuasiquote?

272

Saya mencoba memanggil Shapelessmakro dari dalam quasiquotedengan Scaladan saya tidak mendapatkan apa yang ingin saya dapatkan.

Makro saya tidak mengembalikan kesalahan apa pun tetapi tidak berkembang Witness(fieldName)menjadiWitness.Lt[String]

val implicits = schema.fields.map { field =>
  val fieldName:String = field.name
  val fieldType = TypeName(field.valueType.fullName)
  val in = TermName("implicitField"+fieldName)
  val tn = TermName(fieldName)
  val cc = TermName("cc")
  q"""implicit val $in = Field.apply[$className,$fieldType](Witness($fieldName), ($cc:   $className) => $cc.$tn)"""
}

Ini Fielddefinisi saya :

sealed abstract class Field[CC, FieldName] {
  val  fieldName: String
  type fieldType

  // How to extract this field
  def  get(cc : CC) : fieldType
}

object Field {
  // fieldType is existencial in Field but parametric in Fied.Aux
  // used to explict constraints on fieldType
  type Aux[CC, FieldName, fieldType_] = Field[CC, FieldName] {
    type fieldType = fieldType_
  }

  def apply[CC, fieldType_](fieldWitness : Witness.Lt[String], ext : CC => fieldType_) : Field.Aux[CC, fieldWitness.T, fieldType_] =
    new Field[CC, fieldWitness.T] {
      val fieldName  : String = fieldWitness.value
      type fieldType = fieldType_
      def get(cc : CC) : fieldType = ext(cc)
    }
}

Dalam hal ini implisit yang saya hasilkan terlihat seperti:

implicit val implicitFieldname : Field[MyCaseClass, fieldWitness.`type`#T]{
  override type fieldType = java.lang.String
}

Jika sudah didefinisikan di luar quasiquoteitu akan menghasilkan sesuatu seperti:

implicit val implicitFieldname : Field.Aux[MyCaseClass, Witness.Lt[String]#T, String] = ...

Adakah sesuatu yang bisa dilakukan?

Roch
sumber
Apakah Anda menggunakan ini dalam anotasi makro? Sudahkah Anda mencoba memberikan anotasi jenis untuk $in(yang menurut saya perlu menggunakan ConstantType)?
Travis Brown
@ TravisBrown ya saya sedang membangun ini menggunakan penjelasan makro (Macro Paradise). Saya telah mencoba memberikan tipe seperti ini:q"""implicit val $in : Field.Aux[$className, Witness.Lt[String]#T, String] = Field.apply[$className,$fieldType](Witness($fieldName), ($cc: $className) => $cc.$tn)"""
Roch
Anda akan memerlukan nama bidang spesifik dalam anotasi jenis, (lihat misalnya posting blog 2.0 sebelumnya saya yang sebelumnya tidak berbentuk di sini untuk contoh penggunaan ConstantType). Apakah Anda memiliki contoh kerja yang lengkap?
Travis Brown

Jawaban:

1

Ini adalah solusi kerja saya menggunakan anotasi makro gaya lama.

import scala.language.experimental.macros
import scala.reflect.macros.blackbox.Context
import scala.annotation.StaticAnnotation

class fieldable extends StaticAnnotation {
  def macroTransform(annottees: Any*): Any = macro fieldableMacro.impl
}

object fieldableMacro {
  def impl(c: Context)(annottees: c.Expr[Any]*): c.Tree = {
    import c.universe._
    annottees.map(_.tree) match {
      case (param @ q"case class $className(..$fields)") :: Nil => {
        val implicits = fields.collect {
          case field @ q"$mods val $tname: $tpt" => q"""
            implicit val $tname = Field.apply[$className,$tpt](
              Witness(${tname.decodedName.toString}), _.$tname
            )"""
        }; q"$param; object ${className.toTermName} {..$implicits}"
      }
    }
  }
}

Tentu saja, ini dapat ditingkatkan dengan menggunakan kuasiquote yang lebih baik, tetapi tujuan saya adalah untuk menunjukkan sesuatu yang sebersih mungkin.

Dapat digunakan sebagai:

@fieldable
case class MyCaseClass(foo: String, bar: Int)

Ini menghasilkan MyCaseClassobjek pendamping yang memerlukan Fieldsimplisit:

implicit val foo = Field.apply[MyCaseClass, String](Witness("foo"), ((x$1) => x$1.foo));
implicit val bar = Field.apply[MyCaseClass, Int](Witness("bar"), ((x$2) => x$2.bar));

Seperti yang sudah ditunjukkan, tanpa contoh kerja yang lengkap, cukup sulit untuk menulis jawaban yang lengkap.

Federico Pellegatta
sumber