无形和注释 [英] Shapeless and annotations

查看:35
本文介绍了无形和注释的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想将一些函数应用于用 MyAnnotation 注释的case类中的字段.这个想法是将类型 T 转换为其通用表示形式,提取注释,压缩,向右(或向左)折叠以重建通用表示形式,最后返回到类型 T .我按照此处和此要点.

I would like to have some function applied to fields in a case class, that are annotated with MyAnnotation. The idea is to transform type T into its generic representation, extract annotations, zip, fold right (or left) to reconstruct a generic representation and finally get back to type T. I followed the answer provided here and this gist.

我正在使用scala 2.11.12和无形状的2.3.3.

I'm using scala 2.11.12 and shapeless 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
    }
  }
}

上面的代码进行编译.但是,在测试中实例化修饰符时:

The code above compiles. However, when instanciating a Modifier in a test:

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

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

测试文件无法编译并出现以下错误:

the test file does not compile and give the following error:

  [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

问题似乎出在正确的文件夹定义上:从 genericModifier 的隐式列表中删除 rightFolder 时,它起作用了:

The problem seems to come from the right folder definition: when removing rightFolder from the list of implicits in genericModifier, then it works:

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

怎么了?

推荐答案

您的代码中存在几个错误:

There are several mistakes in your code:

  • 仅为 Option 定义 Poly 太粗糙了(模式匹配在运行时执行,并且编译器应该知道 Some (在编译时)

  • defining Poly just for Option is too rough (pattern matching is performed at runtime and compiler should know definitions for Some and None at compile time)

HNil 应该代替 HNil.type HNil:HNil 代替 HNil ( HNil HNil.type 类型不同)

HNil should be instead of HNil.type and HNil : HNil instead of HNil (types HNil and HNil.type are different)

编译器不知道 RightFolder 实际上会返回原始的 HList 类型,因此您应该使用 RightFolder.Aux 类型.

compiler doesn't know that RightFolder actually returns the original HList type, so you should use RightFolder.Aux type.

正确的代码是

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

这篇关于无形和注释的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆