【发布时间】:2018-04-21 14:38:41
【问题描述】:
我是 Shapless 的新手。
我正在尝试编写一个函数,该函数将采用不同类型序列的HList,将其转换为包含原始HList 元素的笛卡尔积的Seq[HList],并迭代结果序列
例如:
val input = Seq(true, false) :: Seq(1,2,3)::Seq("foo", "bar") :: HNil
cartesianProduct: Seq[Boolean :: Int :: String :: HNil] = Seq(
true :: 1 :: foo :: HNil,
true :: 1 :: bar :: HNil,
true :: 2 :: foo :: HNil,
true :: 2 :: bar :: HNil,
true :: 3 :: foo :: HNil,
true :: 3 :: bar :: HNil,
false :: 1 :: foo :: HNil,
false :: 1 :: bar :: HNil,
false :: 2 :: foo :: HNil,
false :: 2 :: bar :: HNil,
false :: 3 :: foo :: HNil,
false :: 3 :: bar :: HNil)
我能够使用以下代码在 Intellij Scala Worksheet 中实现这一点:
import shapeless._
import shapeless.ops.hlist.LeftFolder
object combine extends Poly {
implicit def `case`[T <: HList, S] = use((acc : Seq[T], curr : Seq[S]) => {
for {
el <- curr
v <- acc
} yield el :: v
})
}
val input = Seq(true, false) :: Seq(1,2,3)::Seq("foo", "bar") :: HNil
val combinations = input.foldLeft(Seq[HNil](HNil))(combine)
combinations.foreach(println)
我认为这里一切正常,因为编译器知道 input 的完整类型。
但是,当我尝试将整个操作包装在一个函数中时,input 的完整类型会丢失,并且我无法在 foldLeft 的结果上调用 foreach:
def cartesian[T <: HList](input: T)
(implicit folder: LeftFolder[T, Seq[HNil], combine.type]) = {
input.foldLeft(Seq[HNil](HNil))(combine)
.foreach(println)
}
编译器报错:
value foreach is not a member of folder.Out
input.foldLeft(Seq[HNil](HNil))(combine).foreach(println)
^
我想有一些隐含的证据我可以要求断言input 的正确形状(Seq[_] 的HList),从而让编译器找出foldLeft 的结果类型,但我无法弄清楚它可能是什么......
希望有人能帮我解决这个问题。 谢谢。
更新:
我对这个问题的最终目标是,给定Seq[_] 的HList 来派生一个函数(可能在案例类上),该函数将接受与输入 HList 具有相同参数的函数和与“Seq”匹配的参数类型' 元素类型的顺序相同。例如,对于函数上方的输入,f: (Boolean, Int, String) => R
因此,实际上我可以使用f 迭代输入的笛卡尔积。
最终代码如下所示:
import shapeless._
import shapeless.ops.function.FnToProduct
import shapeless.ops.hlist.LeftFolder
import shapeless.syntax.std.function.fnHListOps
object combine extends Poly {
implicit def `case`[EL <: HList, S] = use((acc : Seq[EL], curr : Seq[S]) => {
for {
el <- curr
v <- acc
} yield el :: v
})
}
case class Cartesian[R <: HList, F, FR](combinations: Seq[R])
(implicit ftp: FnToProduct.Aux[F, R => Unit]) {
def foreach(f: F) = combinations.foreach(f.toProduct)
}
def cartesian[T <: HList, R <: HList, F, FR](variants: T)(implicit
folder: LeftFolder.Aux[T, Seq[HNil], combine.type, _ <: Seq[R]],
fnToProd: FnToProduct.Aux[F, R => Unit]
) = {
val combinations: Seq[R] = variants.foldLeft(Seq[HNil](HNil))(combine)
Cartesian(combinations)
}
val variants = Seq(true, false) :: Seq("foo", "bar") :: Seq(1, 2, 3) :: HNil
cartesian(variants).foreach((a, b, c) => println(s"$a, $b, $c"))
请注意,函数参数a、b、c 的类型被正确推断为Boolean、String 和Int。
目前,传递给foreach 的函数的结果类型必须是固定的(在上面的代码中是Unit)。无法从传入的函数中推断出来。
【问题讨论】:
-
你能提供你正在使用的进口吗?
-
当然:
import shapeless._import shapeless.ops.hlist.LeftFolder