1
votes

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.)

1
The code you've provided does compile on both 2.12 and 2.11.thesamet
@thesamet You are right. A call to Proc.make() is missing to illustrate the problem.Jasper-M
Actually make is not generic so implicit m is pretty redundant, but that's beside the point here.Jasper-M
@Jasper-M Thanks for the answer! Would something similar work if each element, in addition, is inside another container? Eg, there's case class Box[+E](elem: E) and I = Box[Water]:: ...; Req = Box[Int] ... ; drinks = Box[Water(10)] ... (If necessary, I can edit the question or ask another one.)schrödingercöder
I would say: try it and let me know :-p But I think it should work.Jasper-M

1 Answers

1
votes

Try using polymorphic methods:

object pour extends Poly1{
  implicit def caseInt[A <: Drink[Int]] =
    at[A](o => o.v)
  implicit def caseDec[A <: Drink[BigDecimal]] =
    at[A](o => o.v)
}