0

我正在尝试使用无形生成一个类型类,用于在编译时返回具有特定注释的案例类字段的值。

给定了一个 Scala 注释案例类和一个通用案例类,该类型类Identity[T] 应返回使用此类注释注释的“单一”属性的值。

trait Identity[T] {
  def apply(t: T): Long
}

final case class Id() extends scala.annotation.StaticAnnotation
case class User(@Id id: Long, account_id: Int, name: String, updated_at: java.time.Instant)

并给出案例类和类型类的实例

val identity   = Identity[User] // implicit summoner

val user = User(1009, 101, "Alessandro", Instant.now())
val value = identity(user) 

我想value回来1009

我试图玩弄下面的代码片段,但我确实得到了计算Symbol带注释字段的名称。

object WithShapeless {

  import MyAnnotations.Id
  import shapeless._
  import shapeless.ops.hlist
  import shapeless.ops.record.Keys
  import shapeless.record._

  // select the field symbol with the desired annotation, if exists
  object Mapper extends Poly1 {
    implicit def some[K <: Symbol]: Case.Aux[(K, Some[Id]), Option[K]] = at[(K, Some[Id])] {
      case (k, _) => Some(k)
    }
    implicit def none[K <: Symbol]: Case.Aux[(K, None.type), Option[K]] = at[(K, None.type)] {
      case (k, _) => Option.empty[K]
    }
  }

  implicit def gen[A, HL <: HList, AL <: HList, KL <: HList, ZL <: HList, ML <: HList](
    implicit
    generic: LabelledGeneric.Aux[A, HL],
    annots: Annotations.Aux[Id, A, AL],
    keys: Keys.Aux[HL, KL],
    zip: hlist.Zip.Aux[KL :: AL :: HNil, ZL],
    mapper: hlist.Mapper.Aux[Mapper.type, ZL, ML],
    ev0: hlist.ToList[ML, Option[Symbol]]
  ): Identity[A] = new Identity[A] {

    val zipped: ZL          = zip(keys() :: annots() :: HNil)
    val annotatedFields: ML = mapper.apply(zipped)

    val symbol: Symbol = annotatedFields.to[List].find(_.isDefined).get match {
      case Some(symbol) => symbol
      case _            => throw new Exception(s"Class  has no attribute marked with @IdAnnot")
    }

    println(s"""
               |zipped: ${zipped}
               |mapped: ${annotatedFields}
               |symbol: $symbol
               |""".stripMargin)

    override def apply(a: A): Long = {
      val repr = generic.to(a)
      val value = repr.get(Witness(symbol)) // compilation fails here

      println(s"""
                 |Generic ${generic.to(a)}
                 |value: $value
      """.stripMargin)
      1
    }
  }
}

我试图变出 aSelector返回值,但编译器失败并显示No field this.symbol.type in record HL.

我无法让它工作!谢谢

4

1 回答 1

0

实际上你不需要LabelledGeneric,因为你不使用密钥。尝试

import java.time.Instant
import shapeless.ops.hlist.{CollectFirst, Zip}
import shapeless.{::, Annotations, Generic, HList, HNil, Poly1}

trait Identity[T] {
  type Out
  def apply(t: T): Out
}
object Identity {
  type Aux[T, Out0] = Identity[T] { type Out = Out0 }
  def instance[T, Out0](f: T => Out0): Aux[T, Out0] = new Identity[T] {
    type Out = Out0
    override def apply(t: T): Out = f(t)
  }

  def apply[T](implicit identity: Identity[T]): Aux[T, identity.Out] = identity

  implicit def mkIdentity[T, HL <: HList, AL <: HList, ZL <: HList](implicit
    generic: Generic.Aux[T, HL],
    annotations: Annotations.Aux[Id, T, AL],
    zip: Zip.Aux[HL :: AL :: HNil, ZL],
    collectFirst: CollectFirst[ZL, Mapper.type],
  ): Aux[T, collectFirst.Out] = 
    instance(t => collectFirst(zip(generic.to(t) :: annotations() :: HNil)))
}

object Mapper extends Poly1 {
  implicit def cse[A]: Case.Aux[(A, Some[Id]), A] = at(_._1)
}

final case class Id() extends scala.annotation.StaticAnnotation
case class User(@Id id: Long, account_id: Int, name: String, updated_at: java.time.Instant)

val identity = Identity[User]

val user = User(1009, 101, "Alessandro", Instant.now())
val value = identity(user) //  1009
于 2021-03-14T17:02:10.560 回答