Scala 从两个HList创建所有对的HList

Scala 从两个HList创建所有对的HList,scala,shapeless,hlist,Scala,Shapeless,Hlist,我在Scala中使用shapeless,我想编写一个函数allPairs,它将接受两个HList并返回所有元素对的HList。例如: import shapeless._ val list1 = 1 :: "one" :: HNil val list2 = 2 :: "two" :: HNil // Has value (1, 2) :: (1, "two") :: ("one", 2) :: ("one", "two") :: HNil val list3 = allPairs(list1,

我在Scala中使用shapeless,我想编写一个函数allPairs,它将接受两个HList并返回所有元素对的HList。例如:

import shapeless._
val list1 = 1 :: "one" :: HNil
val list2 = 2 :: "two" :: HNil
// Has value (1, 2) :: (1, "two") :: ("one", 2) :: ("one", "two") :: HNil
val list3 = allPairs(list1, list2)
你知道怎么做吗


另外,我想强调的是,我在寻找一个函数,而不是一个内联代码块。

你不能使用
来理解
-或者将
map
flatMap
与函数文本结合在一起(正如其他答案所示),因为
HList
上的这些方法需要。如果您只有两个静态类型的列表,这很简单:

import shapeless._

val xs = 1 :: 'b :: 'c' :: HNil
val ys = 4.0 :: "e" :: HNil

object eachFirst extends Poly1 {
  implicit def default[A] = at[A] { a =>
    object second extends Poly1 { implicit def default[B] = at[B](a -> _) }
    ys map second
  }
}

val cartesianProductXsYs = xs flatMap eachFirst
这为我们提供了以下信息(适当键入):

编写一个使用
HList
参数的方法要复杂得多。这里有一个简单的例子,说明如何使用一些稍微通用的机器来实现这一点

我首先要指出的是,我们可以将寻找两个普通列表的笛卡尔积看作是“提升”一个函数,该函数接受两个参数,并将它们作为元组返回到列表的应用函子中。例如,您可以编写:

我们可以在这里编写对应于
(,)
的多态二进制函数:

import shapeless._

object tuple extends Poly2 {
  implicit def whatever[A, B] = at[A, B] { case (a, b) => (a, b) }
}
并再次定义示例列表以确保完整性:

val xs = 1 :: 'b :: 'c' :: HNil
val ys = 4.0 :: "e" :: HNil
现在,我们将致力于一个名为
liftA2
的方法,该方法将允许我们编写以下内容:

liftA2(tuple)(xs, ys)
并得到正确的结果。名称
liftA2
有点误导,因为我们没有真正的应用程序函子实例,而且它不是泛型的——我正在研究
HList
上名为
flatMap
map
的方法模型,我愿意接受更好的建议

现在我们需要一个类型类,它允许我们获取一个
Poly2
,将其部分应用于某个对象,并将结果一元函数映射到
HList

trait ApplyMapper[HF, A, X <: HList, Out <: HList] {
  def apply(a: A, x: X): Out
}

object ApplyMapper {
  implicit def hnil[HF, A] = new ApplyMapper[HF, A, HNil, HNil] {
    def apply(a: A, x: HNil) = HNil
  }
  implicit def hlist[HF, A, XH, XT <: HList, OutH, OutT <: HList](implicit
    pb: Poly.Pullback2Aux[HF, A, XH, OutH],
    am: ApplyMapper[HF, A, XT, OutT]
  ) = new ApplyMapper[HF, A, XH :: XT, OutH :: OutT] {
    def apply(a: A, x: XH :: XT) = pb(a, x.head) :: am(a, x.tail)
  }
}

正如我们所希望的。

这很好。您能说明如何将其打包到以xs和ys为参数的方法中吗?我试着这样做,但严重失败。@RégisJean Gilles:这是一个好问题,事实证明,你需要更多的机器来将其推广到一种方法。我现在尝试一下。这个问题要求一个函数;“我认为这是最困难的部分。”埃姆克里斯汀森:好的,看看迈尔斯·萨宾的回答。我今天早上出门的时候就开始做了,今天晚上会更新(除非别人先得到)。干得好。我希望我能再次投票。再说一次,我不确定我是否应该为看到结果有多复杂而激动。我想元编程的快乐和痛苦。顺便问一下,有没有关于无形状的文档?在试图解决这个难题时,部分困难在于逆向工程——拉回1aux、Case1、Case2以及生活在变形动物园中的所有有趣动物的目的。你究竟在“函数”和“内联代码块”之间有什么区别@兰德尔·舒尔茨:看看下面特拉维斯的第一个答案和他使用liftA2的最终答案之间的区别。
liftA2(tuple)(xs, ys)
trait ApplyMapper[HF, A, X <: HList, Out <: HList] {
  def apply(a: A, x: X): Out
}

object ApplyMapper {
  implicit def hnil[HF, A] = new ApplyMapper[HF, A, HNil, HNil] {
    def apply(a: A, x: HNil) = HNil
  }
  implicit def hlist[HF, A, XH, XT <: HList, OutH, OutT <: HList](implicit
    pb: Poly.Pullback2Aux[HF, A, XH, OutH],
    am: ApplyMapper[HF, A, XT, OutT]
  ) = new ApplyMapper[HF, A, XH :: XT, OutH :: OutT] {
    def apply(a: A, x: XH :: XT) = pb(a, x.head) :: am(a, x.tail)
  }
}
trait LiftA2[HF, X <: HList, Y <: HList, Out <: HList] {
  def apply(x: X, y: Y): Out
}

object LiftA2 {
  implicit def hnil[HF, Y <: HList] = new LiftA2[HF, HNil, Y, HNil] {
    def apply(x: HNil, y: Y) = HNil
  }

  implicit def hlist[
    HF, XH, XT <: HList, Y <: HList,
    Out1 <: HList, Out2 <: HList, Out <: HList
  ](implicit
    am: ApplyMapper[HF, XH, Y, Out1],
    lift: LiftA2[HF, XT, Y, Out2],
    prepend : PrependAux[Out1, Out2, Out]
  ) = new LiftA2[HF, XH :: XT, Y, Out] {
    def apply(x: XH :: XT, y: Y) = prepend(am(x.head, y), lift(x.tail, y))
  }
}
def liftA2[HF, X <: HList, Y <: HList, Out <: HList](hf: HF)(x: X, y: Y)(implicit
  lift: LiftA2[HF, X, Y, Out]
) = lift(x, y)
scala> type Result =
     |   (Int, Double) :: (Int, String) ::
     |   (Symbol, Double) :: (Symbol, String) ::
     |   (Char, Double) :: (Char, String) :: HNil
defined type alias Result

scala> val res: Result = liftA2(tuple)(xs, ys)
res: Result = (1,4.0) :: (1,e) :: ('b,4.0) :: ('b,e) :: (c,4.0) :: (c,e) :: HNil