Wie verwende ich Shapeless in einem Quasiquote?

272

Ich versuche, ein ShapelessMakro von innen quasiquotemit aufzurufen, Scalaund bekomme nicht das, was ich gerne hätte.

Mein Makro gibt keine Fehler zurück, erweitert sich jedoch nicht Witness(fieldName)inWitness.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)"""
}

Hier ist meine FieldDefinition:

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)
    }
}

In diesem Fall sieht das von mir generierte Implizit wie folgt aus:

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

Wenn es außerhalb von a definiert worden quasiquotewäre, würde es etwas erzeugen wie:

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

Gibt es etwas, das getan werden kann?

Roch
quelle
Verwenden Sie dies in einer Makroanmerkung? Haben Sie versucht, eine Typanmerkung bereitzustellen $in( für die meiner Meinung nach die Verwendung erforderlich ist ConstantType)?
Travis Brown
@TravisBrown Ja, ich erstelle dies mit einer Makroanmerkung (Macro Paradise). Ich habe versucht, einen Typ wie diesen bereitzustellen:q"""implicit val $in : Field.Aux[$className, Witness.Lt[String]#T, String] = Field.apply[$className,$fieldType](Witness($fieldName), ($cc: $className) => $cc.$tn)"""
Roch
Sie benötigen jedoch den spezifischen Feldnamen in der Typanmerkung ( ein Beispiel für die Verwendung finden Sie in meinem alten Blog-Beitrag vor Shapeless 2.0 hierConstantType ). Haben Sie zufällig ein vollständiges Arbeitsbeispiel?
Travis Brown

Antworten:

1

Dies ist meine Arbeitslösung mit Makroanmerkungen im alten Stil.

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}"
      }
    }
  }
}

Es kann sicher mit besseren Quasiquoten verbessert werden, aber mein Ziel war es, etwas so sauberes wie möglich zu zeigen.

Es kann verwendet werden als:

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

Dies erzeugt ein MyCaseClassBegleitobjekt mit erforderlichen FieldsImplikationen:

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));

Wie bereits erwähnt, ist es ohne ein vollständiges Arbeitsbeispiel ziemlich schwierig, eine vollständige Antwort zu schreiben.

Federico Pellegatta
quelle