I'm trying to map a poly1 function over a shapeless HList. Its elements are subclasses of a parameterised trait. However, I get the error "couldn't find implicit value for the Mapper". Here's a basic example:
import shapeless._
trait Drink[+A]{
def v: A
}
case class Water(v: Int) extends Drink[Int]
case class Juice(v: BigDecimal) extends Drink[BigDecimal]
case class Squash(v: BigDecimal) extends Drink[BigDecimal]
object pour extends Poly1{
implicit def caseInt: Case.Aux[Drink[Int], Int] =
at(o => o.v)
implicit def caseDec: Case.Aux[Drink[BigDecimal], BigDecimal] =
at(o => o.v)
}
object Proc {
type I = Water ::Squash ::Juice :: HNil
type Req = Int ::BigDecimal ::BigDecimal :: HNil
val drinks: I = Water(10)::Squash(15.0):: Juice(1.0)::HNil
def make()(implicit m: ops.hlist.Mapper.Aux[pour.type, I, Req]): Req = { drinks.map(pour)}
}
Running this code produces
Error:(21, 27) could not find implicit value for parameter m: shapeless.ops.hlist.Mapper.Aux[pour.type,Proc.I,Proc.Req]
Although this appears like a simple problem, I haven't found (or recognised) a solution in other answers. My current workaround is to define a case, in poly
, for each subclass of Drink
. This is obviously not appropriate with many subclasses of the trait.
Could there be a better solution (perhaps with TypeTags)?
UPDATE
A general answer to this question, for any (reasonable) Poly1
function, is given by @Jasper_M. (The question is further generalised in Using shapeless HLists with invariant containers.) For the specific transformation I => Req
in the above example a simpler solution is
import syntax.std.tuple._
import poly._
def makeTwo(): Req = (drinks.tupled flatMap identity).productElements
which gives 10 :: 15.0 :: 1.0 :: HNil
. (Note that productElements
is wrongly flagged as error in Intellij 2017.2.6. Moreover, the "untupled" version drinks flatMap identity
causes an "implicit not found" error.)