I am trying to create a type class that allows to select a field for a given type. This is what I have done so far but the compiler is unable to find the 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 = ???
}
}
It will be used like this
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
This should just work when there is one and only one field with type AddressKey. Any idea on how to implement it?
ops.hlist.Selector
doesn't have an Aux
, so I'm assuming you're using that one. Seeing as you want to extract the field name you probably want to use LabelledGeneric
and ops.record.Selector
instead. But then Selector.Aux[Repr, K, AddressKey]
would still not work because Selector
can only search by "key" (K
) and not by "value" (AddressKey
) while you are searching for the key by the value.
Ideally I think you would implement it like this:
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
}
}
But for some reason that doesn't work.
So I think your best bet is implementing the search by value yourself. It's relatively straightforward. You can take inspiration from the ops.hlist.Selector
. Keeping in mind that every entry in the Hlist
that is emitted by LabelledGeneric
is encoded as labelled.FieldType[Key,Value]
and that you can get the runtime value of Key
(or in other words: the field name) with Witness.Aux[Key]
.