使用通用特征的子类在HList上进行映射

时间:2018-01-05 19:58:52

标签: scala shapeless

我正在尝试将poly1函数映射到无形HList上。它的元素是参数化特征的子类。但是,我收到错误“无法找到Mapper的隐含值”。这是一个基本的例子:

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

}

运行此代码会产生 Error:(21, 27) could not find implicit value for parameter m: shapeless.ops.hlist.Mapper.Aux[pour.type,Proc.I,Proc.Req]

虽然这看起来像一个简单的问题,但我还没有在其他答案中找到(或认可)解决方案。我目前的解决方法是在poly中为Drink的每个子类定义一个案例。这显然不适合该特征的许多子类。 可能有更好的解决方案(也许使用TypeTags)?

更新

对于任何(合理的)Poly1函数,这个问题的一般答案由@Jasper_M给出。 (问题在Using shapeless HLists with invariant containers中进一步概括。)对于上例中的特定转换I => Req,更简单的解决方案是

import syntax.std.tuple._
import poly._

def makeTwo(): Req =  (drinks.tupled flatMap identity).productElements

给出了10 :: 15.0 :: 1.0 :: HNil。 (请注意,productElements在Intellij 2017.2.6中被错误地标记为错误。此外,“untupled”版本drinks flatMap identity会导致“隐式未找到”错误。)

1 个答案:

答案 0 :(得分:1)

尝试使用多态方法:

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