How to infer the internal type of a Shapeless record value using a unary type constructor?

I'm having trouble understanding how Shapeless record selector interacts with the scala output type. I am trying to create a method that can capture a field from a Shapeless record by key only if the field value has a certain unary type constructor, in this particular case Vector[_] , and then capture the internal value of the output type V from Vector , in this case with Vector.apply() .

I feel like I'm around. This works with a specific Int type:

 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 make a conclusion about the internal type, this will not work:

 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 complete 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 was able to do is the following:

 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 

What should be safe, huh? But pattern matching does not seem very idiomatic for the formless, and I wonder why a more concise approach with inference does not work. Is there a cleaner way to do this?

+5
source share
1 answer

Scala really poorly describes type inference in such cases (where you want to unify the result of a functional dependency and something like Vector[V] and have output V ).

You can help the compiler in the process by following these steps:

 import shapeless._, ops.record.Selector, syntax.singleton._ def getValueFromVectorField[L <: HList, VS, V]( l: L, fieldName: Witness, index: Int )(implicit sel: Selector.Aux[L, fieldName.T, VS], ev: VS <:< Vector[V] ): V = sel(l).apply(index) val record = ( "a" ->> Vector(0,2,4) ) :: ( "b" ->> Set(1,3,5) ) :: HNil getValueFromVectorField(record,"a",1) // Returns 1, Yay! getValueFromVectorField(record,"b",0) // Does not compile, as intended 

Now it will first output VS and then find out that VS is a subtype of Vector[V] instead of doing both in one step.

This is the same as your version of Unpack1 , except that Unpack1 only proves that T is Vector[V] -it does not actually give you a way to get Vector[V] from T (unlike <:< , what is he doing).

So, your version of Unpack1 safe, in the sense that you can convince yourself that it provides all the evidence you need, but itโ€™s not in the form that the compiler understands, so you need to match the pattern. <:< better because the compiler understands this, but also because it is more recognizable as a workaround for this limitation, since it is provided by the standard library, etc.

+6
source

Source: https://habr.com/ru/post/1258949/


All Articles