Выберите поле по типу

Я пытаюсь создать класс типа, который позволяет выбрать поле для данного типа. Это то, что я сделал до сих пор, но компилятор не может найти Selector.Aux

case class AddressKey(street: String, city: String)

trait AddressKeySelector[A] {
  def addressKey(v: A): AddressKey
  def addressKeyColumnName: String
}

object AddressKeySelector {

  implicit def typeAddressKeySelector[A, Repr <: HList, K](
      implicit gen: Generic.Aux[A, Repr],
      reprSelector: Selector.Aux[Repr, K, AddressKey]): AddressKeySelector[A] =
    new AddressKeySelector[A] {
      override def addressKey(v: A): AddressKey = reprSelector(gen.to(v))

      override def addressKeyColumnName: String = ???
    }
}

Будет использоваться вот так

case class MyType(addressKeyField: AddressKey, otherField: String)
val data = MyType(AddressKey("addr", "city"), "other")
val addrKey = AddressKeySelector[MyType].addressKey(data) 
// addrKey: AddressKey = AddressKey(addr,city) 
val addrField = AddressKeySelector[MyType].addressKeyColumnName(data)
// addrField: String = addressKeyField

Это должно просто работать, когда есть одно и только одно поле с типом AddressKey. Есть идеи, как это реализовать?

1 ответ

Решение

ops.hlist.Selector не имеет Aux так что я предполагаю, что вы используете это. Видя, как вы хотите извлечь имя поля, которое вы, вероятно, хотите использовать LabelledGeneric а также ops.record.Selector вместо. Но потом Selector.Aux[Repr, K, AddressKey] все равно не будет работать, потому что Selector можно искать только по "ключу" (K) а не по "стоимости" (AddressKey) пока вы ищете ключ по значению.

В идеале я думаю, что вы бы реализовали это так:

import shapeless._, ops.record._

trait AddressKeySelector[A] {
  def addressKey(v: A): AddressKey
  def addressKeyColumnName: String
}

object AddressKeySelector {
  def apply[A](implicit sel: AddressKeySelector[A]): sel.type = sel

  implicit def typeAddressKeySelector[A, Repr <: HList, K <: Symbol, Swapped <: HList](
      implicit 
      gen: LabelledGeneric.Aux[A, Repr],
      swap: SwapRecord.Aux[Repr, Swapped],
      swappedSelector: Selector.Aux[Swapped, AddressKey, K],
      reprSelector: Selector.Aux[Repr, K, AddressKey]
  ): AddressKeySelector[A] =
    new AddressKeySelector[A] {
      override def addressKey(v: A): AddressKey = reprSelector(gen.to(v))

      override def addressKeyColumnName: String = swappedSelector(swap()).name
    }
}

Но по какой-то причине это не работает.

Поэтому я думаю, что вам лучше всего реализовать поиск по значению самостоятельно. Это относительно просто. Вы можете черпать вдохновение из ops.hlist.Selector, Помня, что каждая запись в Hlist что испускается LabelledGeneric кодируется как labelled.FieldType[Key,Value] и что вы можете получить значение времени выполнения Key (или другими словами: имя поля) с Witness.Aux[Key],

Другие вопросы по тегам