使用具有不变容器的无形 HList
Using shapeless HLists with invariant containers
假设 HList
的元素是通用特征的子类。每个元素都包含在 case class Box[E](elem E)
中。 Box
在 E
中是 不变的 会导致将 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的答案应用到。
这段代码导致
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]
来解决问题,但在我的项目中是不可能的。一个天真的解决方案——在 Drink
的每个子类的 pour
中都有一个案例——无法扩展。 (这可以通过将单态函数传递给 pour
来实现,我不知道如何实现。)
在此设置中是否有更明智的方法来映射或过滤 HList?
在这种情况下,您所有的外部类型构造函数都是 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-bounded 多态性,但这可能会让我们走得太远。
假设 HList
的元素是通用特征的子类。每个元素都包含在 case class Box[E](elem E)
中。 Box
在 E
中是 不变的 会导致将 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的答案应用到
这段代码导致
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]
来解决问题,但在我的项目中是不可能的。一个天真的解决方案——在 Drink
的每个子类的 pour
中都有一个案例——无法扩展。 (这可以通过将单态函数传递给 pour
来实现,我不知道如何实现。)
在此设置中是否有更明智的方法来映射或过滤 HList?
在这种情况下,您所有的外部类型构造函数都是 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-bounded 多态性,但这可能会让我们走得太远。