Я хотел бы создать общую версию следующего кода:
У меня есть класс дела и функция шифрования
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])
У меня такая же проблема.
Интересно, как решить эту проблему.





Я придумал другой подход.
Вместо того, чтобы делать все сразу, вы можете разбить его на более мелкие части и использовать 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 для HList проверяет, находится ли Symbol's name заголовка HList в предоставленных полях, если да, он применяет encypt, в противном случае он его пропускает.
Использование LabelledGeneric для работы на любом case class
Чтобы предоставить тот же интерфейс:
trait PayloadEncryptor[T] {
def encrypt(source: T, encrypt: String => String): T
}
object PayloadEncryptor {
def forClass[T](fieldNames: String*)(implicit encryptor: Encryptor[T]): PayloadEncryptor[T] = new PayloadEncryptor[T] {
override def encrypt(source: T, encrypt: String => String): T = {
encryptor.encryptFields(source, encrypt, fieldNames)
}
}
}