Come usare Shapeless in un Quasiquote?


272

Sto cercando di chiamare una Shapelessmacro dall'interno di un quasiquotecon Scalae non sto ottenendo quello che vorrei ottenere.

La mia macro non restituisce alcun errore ma non si espande 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)"""
}

Ecco la mia Fielddefinizione:

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 questo caso l'implicito che ho generato è simile a:

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

Se fosse stato definito all'esterno di un quasiquote, genererebbe qualcosa del tipo:

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

C'è qualcosa che si può fare?


Lo stai usando in un'annotazione macro? Hai provato a fornire un'annotazione di tipo per $in(che penso che richiederà l'utilizzo ConstantType)?
Travis Brown,

@TravisBrown sì Sto costruendo questo usando un'annotazione macro (Macro Paradise). Ho provato a fornire un tipo come questo:q"""implicit val $in : Field.Aux[$className, Witness.Lt[String]#T, String] = Field.apply[$className,$fieldType](Witness($fieldName), ($cc: $className) => $cc.$tn)"""
Roch,

Tuttavia, avrai bisogno del nome del campo specifico nell'annotazione del tipo (vedi ad esempio il mio vecchio post sul blog pre-Shapeless 2.0 qui per un esempio di utilizzo ConstantType). Ti capita di avere un esempio di lavoro completo in giro?
Travis Brown,

Risposte:


1

Questa è la mia soluzione di lavoro che utilizza annotazioni macro vecchio stile.

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

Di sicuro può essere migliorato usando quasiquotazioni migliori, ma il mio obiettivo era mostrare qualcosa di più pulito possibile.

Può essere usato come:

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

Questo produce un MyCaseClassoggetto compagno che ha richiesto Fieldsimpliciti:

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

Come è già stato sottolineato, senza un esempio di lavoro completo, è abbastanza difficile scrivere una risposta esaustiva.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.