Is it possible, using Shapeless, to extract a value of a specific type from a case class? So far, I can do this:
def fromCaseClass[T, R <: HList](value: T)(implicit ga: Generic.Aux[T, R]): R = {
ga.to(value)
}
Which then allows me to extract values procedurally:
scala> case class ServiceConfig(host: String, port: Int, secure: Boolean)
defined class ServiceConfig
scala> val instance = ServiceConfig("host", 80, true)
instance: ServiceConfig = ServiceConfig(host,80,true)
scala> fromCaseClass(instance).select[Boolean]
res10: Boolean = true
scala> fromCaseClass(instance).select[Int]
res11: Int = 80
However, when I try to write a function to do this, I'm getting tied up with implicits not found:
def getByType[C, X](value: C)(implicit ga: Generic.Aux[C, X]): X = {
fromCaseClass(value).select[X]
}
<console>:12: error: could not find implicit value for parameter ga: shapeless.Generic.Aux[C,R]
fromCaseClass(value).select[X]
Presumably I'm getting this because the compiler can't verify that my parameter is not a case class. Is there a way for me to do this?
I'm quite new to Shapeless, so I'm not entirely sure if I'm trying to do something crazy or if missing something simple.
Update
I feel I'm getting a bit closer. I can implement as so:
def newGetByType[C, H <: HList, R]
(value: C)
(implicit ga: Generic.Aux[C, H], selector: Selector[H, R]): R = {
ga.to(value).select[R]
}
And this allows me to select from a case class:
scala> val s: String = newGetByType(instance)
s: String = host
But this only seems to work for the first type in the case class:
scala> val i: Int = newGetByType(instance)
<console>:17: error: type mismatch;
found : String
required: Int
val i: Int = newGetByType(instance)
Am I on the right track?
You were getting close ...
The main problem with your
newGetByType
is that it doesn't give you any way to explicitly specify the type you're hoping to extract (and, as you've observed, typing the val on the LHS isn't sufficient to allow it to be inferred).Why can't you explicitly specify the type to be extracted? Here's the definition again,
Ideally we'd like to be able to specify the type argument
S
, allowC
to be inferred from thevalue
argument, andL
to be computed by implicit resolution fromC
. Unfortunately, however, Scala doesn't allow us to partially specify type arguments ... it's all or nothing.So the trick to get this to work is to split the type parameter list into two: one which can be fully specified explicitly and one which can be fully inferred: this is a general technique, not one which is specific to shapeless.
We do this by moving the main part of the computation to an auxiliary class which is parametrized by the type we are going to supply explicitly,
Notice that here we can now assume that
S
is known, and both of the type arguments toapply
can be inferred. We finish up with the now trivial definition ofgetByType
which just provides a place where the explicit type argument can go, and instantiates the auxiliary class,This gives you the result you want,