I am playing around with shapeless to explore the possibility of extracting the value type in a shapeless labeled record. The motivation is that I can then use type classes and implicits to dispatch the process flow based on the type. However, the following code is not working.
import shapeless._, record._
import shapeless.ops.record.Selector
import shapeless.syntax.RecordOps
import shapeless.syntax.singleton._
object ShapelessRecordTest extends App {
trait Extractor[K, T] {
type OUT_K
def extract: T => OUT_K
}
object Extractor {
type Aux[K, T, OUT_K0] = Extractor[K, T] {
type OUT_K = OUT_K0
}
implicit def apply[T, Repr, OUT](k: Witness)(
implicit gen: LabelledGeneric.Aux[T, Repr],
selector: Selector.Aux[Repr, k.T, OUT]
): Aux[k.T, T, OUT] = new Extractor[k.T, T] {
type OUT_K = OUT
def extract: T => OUT_K = (e: T) => {
val rep = gen.to(e)
val rprOp: RecordOps[Repr] = new RecordOps[Repr](rep)
rprOp(k)
}
}
}
case class Person(name: String, address: String, age: Int)
implicit val gen = LabelledGeneric[Person]
val nameWit: Witness = 'name
val nameExtractor: Extractor.Aux[nameWit.T, Person, String] = Extractor(nameWit)
def main(args: Array[String]): Unit = {
val joe = Person("Joe", "Brighton", 33)
println(nameExtractor.extract(joe))
}
}
I am using Scala 2.12.8 and the compiler error is
{
"resource": "/workspace/connecterra/stream-data-pipeline/flink/src/main/scala/io/connecterra/ShapelessRecordTest.scala",
"owner": "_generated_diagnostic_collection_name_#0",
"severity": 8,
"message": "could not find implicit value for parameter selector: shapeless.ops.record.Selector.Aux[Repr,ShapelessRecordTest.nameWit.T,OUT]",
"source": "scalac",
"startLineNumber": 37,
"startColumn": 74,
"endLineNumber": 37,
"endColumn": 75
}
I'd like to understand what I am missing here. Or is it possible at all in shapeless?
There's not much sense to make
apply
implicit if you're going to call it explicitly. It's better not to mix materializing method and implicit method defining instances of type class.RecordOps
are not supposed to be created manually (there isimport shapeless.record._
for that). Sometimes it's better to callapply
method of a type class (e.g.shapeless.ops.record.Selector
) directly rather than to rely on extension methods. When you writeval nameWit: Witness = 'name
you loose type refinement. There is no sense to createimplicit val gen = LabelledGeneric[Person]
. ForSelector
,Repr
should be<: HList
.Try