Бесформенные и аннотации - PullRequest
0 голосов
/ 29 января 2019

Я бы хотел, чтобы к полям в классе дел применялась некоторая функция, помеченная MyAnnotation.Идея состоит в том, чтобы преобразовать тип T в его общее представление, извлечь аннотации, сжать, согнуть вправо (или влево), чтобы восстановить общее представление и, наконец, вернуться к типу T.Я следовал за предоставленным ответом здесь , и это суть .

Я использую Scala 2.11.12 и бесформенный 2.3.3.

Здесь и далеемой код:

import shapeless._
import shapeless.ops.hlist._

case class MyAnnotation(func: String) extends scala.annotation.StaticAnnotation

trait Modifier[T] {
  def modify(t: T): T
}

object Modifier {

  def apply[A: Modifier]: Modifier[A] = implicitly[Modifier[A]]

  def create[T](func: T => T): Modifier[T] = new Modifier[T] { override def modify(t: T): T = func(t) }

  private def id[T](t: T) = t

  implicit val stringModifier: Modifier[String] = create(id)

  implicit val booleanModifier: Modifier[Boolean] = create(id)

  implicit val byteModifier: Modifier[Byte] = create(id)

  implicit val charModifier: Modifier[Char] = create(id)

  implicit val doubleModifier: Modifier[Double] = create(id)

  implicit val floatModifier: Modifier[Float] = create(id)

  implicit val intModifier: Modifier[Int] = create(id)

  implicit val longModifier: Modifier[Long] = create(id)

  implicit val shortModifier: Modifier[Short] = create(id)

  implicit val hnilModifier: Modifier[HNil] = create(id)

  implicit def hlistModifier[H, T <: HList, AL <: HList](
    implicit
    hser: Lazy[Modifier[H]],
    tser: Modifier[T]
  ): Modifier[H :: T] = new Modifier[H :: T] {
    override def modify(ht: H :: T): H :: T = {
      ht match {
        case h :: t =>
          hser.value.modify(h) :: tser.modify(t)
      }
    }
  }

  implicit val cnilModifier: Modifier[CNil] = create(id)

  implicit def coproductModifier[L, R <: Coproduct](
    implicit
    lser: Lazy[Modifier[L]],
    rser: Modifier[R]
  ): Modifier[L :+: R] = new Modifier[L :+: R] {
    override def modify(t: L :+: R): L :+: R = t match {
      case Inl(l) => Inl(lser.value.modify(l))
      case Inr(r) => Inr(rser.modify(r))
    }
  }

  object Collector extends Poly2 {
    implicit def myCase[ACC <: HList, E] = at[(E, Option[MyAnnotation]), ACC] {
      case ((e, None), acc) => e :: acc
      case ((e, Some(MyAnnotation(func))), acc) => {
        println(func)
        e :: acc
      }
    }
  }

  implicit def genericModifier[T, HL <: HList, AL <: HList, ZL <: HList](
    implicit
    gen: Generic.Aux[T, HL],
    ser: Lazy[Modifier[HL]],
    annots: Annotations.Aux[MyAnnotation, T, AL],
    zip: Zip.Aux[HL :: AL :: HNil, ZL],
    rightFolder: RightFolder[ZL, HNil.type, Collector.type]
  ): Modifier[T] = new Modifier[T] {
    override def modify(t: T): T = {
      val generic = gen.to(t)
      println(generic)
      val annotations = annots()
      println(annotations)
      val zipped = zip(generic :: annotations :: HNil)
      println(zipped)
      val modified = zipped.foldRight(HNil)(Collector)
      println(modified)

      val typed = gen.from(generic) // temporary
      typed
    }
  }
}

Код выше компилируется.Однако при создании экземпляра Modifier в тесте:

  case class Test(a: String, @MyAnnotation("sha1") b: String)

  val test = Test("A", "B")
  val modifier: Modifier[Test] = implicitly

тестовый файл не компилируется и выдает следующую ошибку:

  [error] ambiguous implicit values:
  [error]  both value StringCanBuildFrom in object Predef of type => 
           scala.collection.generic.CanBuildFrom[String,Char,String]
  [error]  and method $conforms in object Predef of type [A]=> <:<[A,A]
  [error]  match expected type T
  [error]       val ser1: Modifier[Test] = implicitly

Возможно, проблема связана сопределение папки: при удалении rightFolder из списка имплицитов в genericModifier, тогда работает:

  implicit def genericModifier[T, HL <: HList, AL <: HList, ZL <: HList](
    implicit
    gen: Generic.Aux[T, HL],
    ser: Lazy[Modifier[HL]],
    annots: Annotations.Aux[MyAnnotation, T, AL],
    zip: Zip.Aux[HL :: AL :: HNil, ZL]/*,
    rightFolder: RightFolder[ZL, HNil.type, Collector.type]*/
  ): Modifier[T] = new Modifier[T] {
    override def modify(t: T): T = {
      val generic = gen.to(t)
      println(generic)
      val annotations = annots()
      println(annotations)
      val zipped = zip(generic :: annotations :: HNil)
      println(zipped)
      /*val modified = zipped.foldRight(HNil)(Collector)
      println(modified)*/

      val typed = gen.from(generic) // temporary
      typed
    }
  }

Что не так?

1 Ответ

0 голосов
/ 29 января 2019

В вашем коде есть несколько ошибок:

  • определение Poly только для Option слишком грубое (сопоставление с образцом выполняется во время выполнения, и компилятор должен знать определения для Some и None во время компиляции)

  • HNil должно быть вместо HNil.type и HNil : HNil вместо HNil (типы HNil и HNil.typeотличается)

  • компилятор не знает, что RightFolder фактически возвращает исходный тип HList, поэтому вы должны использовать тип RightFolder.Aux.

Правильный код

import shapeless.ops.hlist.{RightFolder, Zip}
import shapeless.{::, Annotations, Generic, HList, HNil, Lazy, Poly2}
import scala.annotation.StaticAnnotation

object App {
  case class MyAnnotation(func: String) extends StaticAnnotation

  object Collector extends Poly2 {
//    implicit def myCase[ACC <: HList, E] = at[(E, Option[PII]), ACC] {
//      case ((e, None), acc) => e :: acc
//      case ((e, Some(MyAnnotation(func))), acc) => {
//        println(func)
//        e :: acc
//      }
//    }

    implicit def someCase[ACC <: HList, E]: Case.Aux[(E, Some[MyAnnotation]), ACC, E :: ACC] = at {
      case ((e, Some(MyAnnotation(func))), acc) =>
        println(func)
        e :: acc
    }

    implicit def noneCase[ACC <: HList, E]: Case.Aux[(E, None.type), ACC, E :: ACC] = at {
      case ((e, None), acc) => e :: acc
    }
  }

  trait Modifier[T] {
    def modify(t: T): T
  }

  implicit def hListModifier[HL <: HList]: Modifier[HL] = identity(_) 
  // added as an example, you should replace this with your Modifier for HList

  implicit def genericModifier[T, HL <: HList, AL <: HList, ZL <: HList](implicit
    gen: Generic.Aux[T, HL],
    ser: Lazy[Modifier[HL]],
    annots: Annotations.Aux[MyAnnotation, T, AL],
    zip: Zip.Aux[HL :: AL :: HNil, ZL],
    rightFolder: RightFolder.Aux[ZL, HNil/*.type*/, Collector.type, HL /*added*/]
    ): Modifier[T] = new Modifier[T] {
    override def modify(t: T): T = {
      val generic = gen.to(t)
      println(generic)
      val annotations = annots()
      println(annotations)
      val zipped = zip(generic :: annotations :: HNil)
      println(zipped)
      val modified = zipped.foldRight(HNil : HNil /*added*/)(Collector)
      println(modified)

      val typed = gen.from(modified)
      typed
    }
  }

  case class Test(a: String, @MyAnnotation("sha1") b: String)

  val test = Test("A", "B")
  val modifier: Modifier[Test] = implicitly[Modifier[Test]]

  def main(args: Array[String]): Unit = {
    val test1 = modifier.modify(test) // prints "sha1"
    println(test1) // Test(A,B)
  }
}
...