как использовать shapeless для определения аннотации типа поля

Я пытаюсь собрать поля класса case, которые имеют определенные аннотации во время компиляции, используя shapeless. Я попытался поиграть со следующим фрагментом, но это не сработало, как ожидалось (ничего не выводилось вместо печати «i»). Как я могу заставить его работать?

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

person Sheng    schedule 12.01.2018    source источник


Ответы (1)


Глядя на макрос Annotation, он отвергает тип, который не является продуктом или сопутствующим продуктом.

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

это весьма прискорбно, так как сбор аннотаций типа на уровне символа поля иногда может быть весьма полезным.

Существует еще один класс типов Annotations, определенный в том же файле, который фактически может собирать определенные аннотации для полей в класс HList. Однако проблема в том, что информация о поле полностью потеряна. Есть неуклюжий способ взломать вещи вместе, чтобы служить моему варианту использования...

// 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)
}
person Sheng    schedule 12.01.2018
comment
Откуда Doc? - person Yuval Itzchakov; 13.01.2018
comment
Поддерживаются только аннотации продуктов или продуктов, так что они могут быть созданы с помощью shapeless во время выполнения (как это делает shapeless.Generic.from) и получены с помощью методов apply классов типов Annotation и Annotations. - person Alex Archambault; 13.01.2018
comment
@al3xar Проблема в том, что Annotations теряет информацию о поле, т. Е. Если одна из аннотаций содержит внутреннее значение, у вас его нет, что обычно вам нужно при обертывании типа пользовательским аннотацией. - person Yuval Itzchakov; 13.01.2018
comment
Тем не менее, это не виновник здесь, ваш searchable имеет экземпляр shapeless.Generic. - person Alex Archambault; 13.01.2018
comment
@YuvalItzchakov Annotations имеет метод применения, который предоставляет экземпляры аннотаций во время выполнения. - person Alex Archambault; 13.01.2018
comment
@al3xar al3xar Но допустим, вы хотите использовать эти аннотации для получения кодека класса case во время компиляции. Это можно сделать? - person Yuval Itzchakov; 13.01.2018
comment
Как вы используете collect? - person Yuval Itzchakov; 13.01.2018
comment
скажем, есть класс case, я могу сказать, что хочу только собрать и превратить аннотированные поля класса case в вектор полей Lucene. B может быть Vector[Field], у которого уже есть пустое значение. func может считывать имя поля, и в зависимости от типа значения поля мы можем превратить их в intfield, longfield, textfield и т. д. - person Sheng; 13.01.2018
comment
@al3xar al3xar, однако, что, если я хочу получить аннотацию определенного поля с типом ключа K во время компиляции для Some[searchable] или None.type ? - person Sheng; 13.01.2018
comment
@Sheng Понятно, но не могли бы вы показать практический пример с использованием класса case? Я пытался сделать что-то подобное в последнее время без успеха. - person Yuval Itzchakov; 13.01.2018
comment
@YuvalItzchakov Я создал суть: gist.github.com/shengc/2b4052ed59419e86f36f48da35812634, она должна быть достаточно просто, чтобы обобщить эту идею на любой класс случаев. Если бы класс бесформенных аннотаций на уровне поля был фиксированным, интерфейс программирования был бы намного проще для понимания и обслуживания. - person Sheng; 14.01.2018
comment
@Sheng Спасибо за суть! Мне трудно понять, кто предоставляет все параметры типа document при его вызове. Я предполагаю, что они могут быть просто выведены компилятором при предоставлении экземпляра Person (я имею в виду параметры типа HL, AL и т. д.). - person Yuval Itzchakov; 14.01.2018
comment
@Sheng Большое спасибо за помощь, Шэн. - person Yuval Itzchakov; 15.01.2018