我想创建以下代码的通用版本:
我有一个案例类和一个加密函数
case class Cat(name: String, age: Int, color: String)
val encrypt : String => String = _.hashCode.toString // as an example
val encryptableFields = Seq("color")
我有Poly1,它将在我的HList
import shapeless._
import labelled._
import record._
trait enc extends Poly1 {
implicit def defaultEncrypt[K,V] = at[(K, V)] { case (k,v) =>field[K](v)}
}
object pol extends enc {
implicit def stringEncrypt[K <: Symbol] = at[(K, String)] { case (k,v) => field[K](if(encryptableFields contains k.name) encrypt(v) else v)}
}
当我使用它时,它按预期工作:
val cat = Cat("name", 1, "black")
val lgCat = LabelledGeneric[Cat]
val la = lgCat.to(cat)
val a = la.fields.map(pol)
lgCat.from(a)
// Cat("name", 1, "93818879")
因为它有效,我正在考虑以通用方式创建它并封装功能和类型类,如:
trait Encryptor[T] {
val fields: Seq[String]
def encryptFields(source: T, encrypt: String => String): T
}
object Encryptor {
def forClass[A <: Product](f: Seq[String]) = new Encryptor[A] {
val fields: Seq[String] = f
override def encryptFields(source:A, encrypt: String => String): A = {
object pol extends enc {
implicit def stringEncrypt[K <: Symbol] = at[(K, String)] { case (k, v) => field[K](if (f contains k.name) encrypt(v) else v) }
}
val gen = LabelledGeneric[A]
val hList = gen.to(source)
val updated = hList.fields.map(pol)
gen.from(updated)
}
}
}
通过这个实现,我得到以下编译时错误:
Error:could not find implicit value for parameter lgen: shapeless.LabelledGeneric[A]
val gen = LabelledGeneric[A]
尝试通过传递LabelledGeneric[A]
隐含地提出更多问题来解决它。
def forClass[A <: Product, R <: HList](f: Seq[String])(implicit gen: implicit gen: LabelledGeneric.Aux[A, R]) = new Encryptor[A] { ... }
抱怨Error:(46, 27) could not find implicit value for parameter fields: shapeless.ops.record.Fields[gen.Repr]; val updated = hList.fields.map(pol)
试图传递一个时:
def forClass[A <: Product, R <: HList, FOut <: HList](f: Seq[String])(
implicit gen: LabelledGeneric.Aux[A, R], fields: Fields.Aux[R, FOut])
我有同样的问题。
我想知道如何克服这个问题。
答案 0 :(得分:3)
我提出了另一种方法。
您可以将其分解为较小的部分,并使用不同的方法在HList
上进行操作,而不是一次性完成所有操作。
让我们为内部表示创建一个类型类:
trait Encryptor[T] {
def encryptFields(source: T, encrypt: String => String, fields: Seq[String]): T
}
在您的示例中,您只有Int
和String
字段,所以我会坚持这一点。
import shapeless._
import labelled._
object Encryptor {
def apply[A](implicit enc: Encryptor[A]): Encryptor[A] = enc
implicit val stringEncryptor: Encryptor[String] = new Encryptor[String] {
override def encryptFields(source: String, encrypt: String => String, fields: Seq[String]) = encrypt(source)
}
implicit val intEncryptor: Encryptor[Int] = new Encryptor[Int] {
override def encryptFields(source: Int, encrypt: String => String, fields: Seq[String]) = source
}
implicit val hnilEncryptor: Encryptor[HNil] = new Encryptor[HNil] {
override def encryptFields(source: HNil, encrypt: String => String, fields: Seq[String]) = HNil
}
implicit def hlistEncryptor[A, K <: Symbol, H, T <: HList](
implicit
witness: Witness.Aux[K],
hEncryptor: Lazy[Encryptor[H]],
tEncryptor: Encryptor[T]
): Encryptor[FieldType[K, H] :: T] = new Encryptor[FieldType[K, H] :: T] {
val fieldName: String = witness.value.name
override def encryptFields(source: FieldType[K, H] :: T, encrypt: String => String, fields: Seq[String]) = {
val tail = tEncryptor.encryptFields(source.tail, encrypt, fields)
val head = if (fields contains fieldName) field[K](hEncryptor.value.encryptFields(source.head, encrypt, fields))
else source.head
head :: tail
}
}
import shapeless.LabelledGeneric
implicit def genericObjectEncryptor[A, H <: HList](
implicit
generic: LabelledGeneric.Aux[A, H],
hEncryptor: Lazy[Encryptor[H]]
): Encryptor[A] = new Encryptor[A] {
override def encryptFields(source: A, encrypt: String => String, fields: Seq[String]) = {
generic.from(hEncryptor.value.encryptFields(generic.to(source), encrypt, fields))
}
}
}
因为在您的示例中,您仅在encrypt
字段上应用String
函数,因此它仅在stringEncrytor
实例中使用。 Encryptor
的{{1}}检查HList
头部的Symbol's name
是否在提供的字段中,如果是,则应用HList
否则会跳过它。
使用encypt
使其适用于任何LabelledGeneric
提供相同的界面:
case class