Shapeless:提取通过注释参数化的大小写字段值



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

它给出了一个Scala注释用例类和一个泛型用例类——类型类Identity[T]应返回用此类注释注释的"single"属性的值。

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

我试图变出一个Selector来返回值,但编译器用No field this.symbol.type in record HL失败了。

我不能让它工作!感谢

实际上您不需要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

最新更新