I'm having trouble understanding the way the Shapeless record Selector interacts with scala's type inference. I'm trying to create a method that can grab a field from a Shapeless record by key, only if the value of the field has a certain unary type constructor, in this particular case Vector[_]
, and then grab an inner value of inferred type V
out of the Vector
, in this case with Vector.apply()
.
I feel like I'm close. This works, with a concrete inner type of Int
:
val record = ( "a" ->> Vector(0,2,4) ) :: ( "b" ->> Set(1,3,5) ) :: HNil
def getIntFromVectorField[L <: HList](l: L, fieldName:Witness, index:Int)(implicit
sel: Selector.Aux[L, fieldName.T, Vector[Int]]
):Int = l(fieldName).apply(index)
getIntFromVectorField(record,"a",1) // Returns 1
getIntFromVectorField(record,"b",0) // Does not compile, as intended
But if I try to infer the inner type, it fails:
def getValueFromVectorField[L <: HList,V](l:L, fieldName:Witness, index:Int)(implicit
sel: Selector.Aux[L,fieldName.T,Vector[V]]
):V = l(fieldName).apply(index) // Compiles
getValueFromVectorField(record,"a",1) // Why does this not compile?
Here's the full error:
could not find implicit value for parameter sel:
shapeless.ops.record.Selector[shapeless.::[scala.collection.immutable.Vector[Int]
with shapeless.labelled.KeyTag[String("a"),scala.collection.immutable.Vector[Int]],
shapeless.::[scala.collection.immutable.Set[Int]
with shapeless.labelled.KeyTag[String("b"),scala.collection.immutable.Set[Int]],
shapeless.HNil]],String("a")]{type Out = scala.collection.immutable.Vector[V]}
What I have been able to do instead is this:
def getValueFromVectorField[L <: HList,T,V](l:L, fieldName:Witness, index:Int)(implicit
sel: Selector.Aux[L,fieldName.T,T],
unpack: Unpack1[T,Vector,V]
):V = l(fieldName) match {
case v:Vector[V] => v.apply(index)
}
getValueFromVectorField(record,"a",1) // Returns 1, Yay!
getValueFromVectorField(record,"b",0) // Does not compile, as intended
Which should be safe, yes? But the pattern matching doesn't feel very idiomatic for shapeless, and I'm wondering why the more concise approach with inference doesn't work. Is there a cleaner way to do this?