how to use shapeless to detect field type annotation

|▌冷眼眸甩不掉的悲伤 提交于 2019-12-22 06:49:40

问题


I am trying to gather the fields of a case class that have a particular annotations at compile time using shapeless. I tried to play around the following snippet, but it did not work as expected (output nothing instead of printing "i"). How can I make it work ?

import shapeless._
import shapeless.labelled._

final class searchable() extends scala.annotation.StaticAnnotation
final case class Foo(@searchable i: Int, s: String)

trait Boo[A] {
  def print(a: A): Unit
}
sealed trait Boo0 {
  implicit def hnil = new Boo[HNil] { def print(hnil: HNil): Unit = () }
  implicit def hlist[K <: Symbol, V, RL <: HList](implicit b: Boo[RL]): Boo[FieldType[K, V] :: RL] =
    new Boo[FieldType[K, V] :: RL] {
      def print(a: FieldType[K, V] :: RL): Unit = {
        b.print(a.tail)
      }
    }
}
sealed trait Boo1 extends Boo0 {
  implicit def hlist1[K <: Symbol, V, RL <: HList](implicit annot: Annotation[searchable, K], witness: Witness.Aux[K], b: Boo[RL]): Boo[FieldType[K, V] :: RL] =
    new Boo[FieldType[K, V] :: RL] {
      def print(a: FieldType[K, V] :: RL): Unit = {
        Console.println(witness.value.name)
        b.print(a.tail)
      }
    }
}
object Boo extends Boo1 {
  implicit def generics[A, HL <: HList](implicit iso: LabelledGeneric.Aux[A, HL], boo: Boo[HL]): Boo[A] =
    new Boo[A] {
      def print(a: A): Unit = {
        boo.print(iso.to(a))
      }
    }
}

implicitly[Boo[Foo]].print(Foo(1, "2"))

回答1:


Looking at the macro of Annotation, it rejects type that is not a product or coproduct straight up

val annTreeOpts =
  if (isProduct(tpe)) { ... }
  else if (isCoproduct(tpe)) { ... }
  else abort(s"$tpe is not case class like or the root of a sealed family of types")

this is quite unfortunate, as collecting type annotations at per field symbol level could be quite useful sometimes.

There is another type class Annotations defined in the same file that can actually collect particular annotations on field into an HList. However problem is the field information is totally lost. There is a clumsy way to hack things together to serve my use case...

// A is our annotation
// B is our result type    
// C is our case class with some fields annotated with A

def empty: B = ???
def concat(b1: B, b2: B): B = ???
def func(a: A, nm: String): B = ???

object Collector extends Poly2 {
   implicit def some[K <: Symbol](implicit witness: Witness.Aux[K]) = 
     at[B, (K, Some[A])] { case (b, (_, a)) => concat(b, func(a.get, witness.value.name)) }
   implicit def none[K <: Symbol] = at[B, (K, None.type)] { case (b, _) => b }
}

def collect[HL <: HList, RL <: HList, KL <: HList, ZL <: HList](implicit 
    iso: LabelledGeneric.Aux[C, HL]
  , annot: Annotations.Aux[A, C, RL]
  , keys: Keys.Aux[HL, KL]
  , zip: Zip.Aux[KL :: RL :: HNil, ZL]
  , leftFolder: LeftFolder.Aux[ZL, B, Collector.type, B]): B = {
  zip(keys() :: annot() :: HNil).foldLeft(empty)(Collector)
}


来源:https://stackoverflow.com/questions/48231348/how-to-use-shapeless-to-detect-field-type-annotation

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!