【问题标题】:Using shapeless HLists with invariant containers将无形 HList 与不变容器一起使用
【发布时间】:2018-01-08 23:48:31
【问题描述】:

假设HList 的元素是通用特征的子类。每个元素都包含在case class Box[E](elem E) 中。 BoxE 中是不变的,这会导致将poly1 映射到HList、按父特征选择元素等问题。下面是一个示例:

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]


case class Box[E](elem: E) // NB! invariance in E

object pour extends Poly1{

  implicit def caseInt[A <: Box[Drink[Int]]] =  at[A](o => Box(o.elem.v * 2))
  implicit def caseDec[A <: Box[Drink[BigDecimal]]] = at[A](o => Box(o.elem.v + 5.0))
}


object Proc {

  type I = Box[Water] :: Box[Squash] :: Box[Juice] ::  HNil
  type O = Box[Int] :: Box[BigDecimal] :: Box[BigDecimal] :: HNil

  val drinks: I = Box(Water(10)) :: Box(Squash(15.0)) :: Box(Juice(2.0)) :: HNil

  def make()(implicit m: ops.hlist.Mapper.Aux[pour.type, I, O]): O = drinks.map(pour)
}


object Main extends App{
  override def main(args: Array[String]): Unit =  Proc.make()
}

*函数pour 将@Jasper_M 的答案应用于Mapping over HList with subclasses of a generic trait

这段代码导致 Error:(38, 22) could not find implicit value for parameter m: shapeless.ops.hlist.Mapper.Aux[pour.type,Proc.I,Proc.O] Proc.make()。 此外,过滤Proc.drinks.covariantFilter[Box[Drink[Int]]] 会产生HNil。 (此过滤器实现了@Travis Brown 对Do a covariant filter on an HList 的回答。)

在我的项目中无法定义解决问题的Box[+E]。一个幼稚的解决方案——在pour 中为Drink 的每个子类提供一个案例——无法扩展。 (这可以通过将单态函数传递给pour 来实现,我不知道怎么做。)

在此设置中是否有更明智的方法来映射或过滤 HList?

【问题讨论】:

    标签: scala shapeless


    【解决方案1】:

    在这种情况下,您的所有外部类型构造函数都是 Box,您可以应用与我之前的回答几乎相同的技术:

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

    现在,如果您的 Box 类型也是多态的,您仍然可以更进一步:

    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]
    
    trait Box[E] { def elem: E}
    case class ABox[E](elem: E) extends Box[E]
    case class BBox[E](elem: E) extends Box[E]
    
    object pour extends Poly1{
      implicit def caseInt[A <: Drink[Int], M[x] <: Box[x]] = at[M[A]](o => o.elem.v * 2)
      implicit def caseDec[A <: Drink[BigDecimal], M[x] <: Box[x]] = at[M[A]](o => o.elem.v + 5.0)
    }
    
    
    val drinks = ABox(Water(10)) :: BBox(Squash(15.0)) :: ABox(Juice(2.0)) :: HNil
    
    drinks.map(pour)
    

    您可能已经注意到,在最后一个示例中,我没有将值重新包装在其框中。你仍然可以这样做,例如,如果你实现类似 trait Boxer[M[_]] { def box[A](a: A): M[A] } 类型类的东西,或者在 Box 中使用 F 有界多态性,但这可能会让我们走得太远。

    【讨论】:

      猜你喜欢
      • 2015-10-24
      • 1970-01-01
      • 2020-04-06
      • 1970-01-01
      • 2015-11-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多