取 Seq[_] 的 HList 并用值的笛卡尔积生成 Seq[HList]
Taking HList of Seq[_] and generating Seq[HList] with cartesian product of values
我是 Shapless 的新手。
我正在尝试编写一个函数,它将采用 HList
不同类型的序列,将其转换为包含原始 HList
的笛卡尔积的 Seq[HList]
' s 元素,并遍历生成的 Sequence
例如:
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
来派生一个函数(可能在 class 的情况下),该函数将接受与输入 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
)。无法从传入的函数中推断出来。
问题不是编译器对输入一无所知,而是它对输出.
一无所知
在 def cartesian
内部,编译器只知道在 foldLeft
之后你得到某种类型 folder.Out
(这取决于 shapeless 会为你计算的实例)。
为了确保结果类型,您可以使用 LeftFolder.Aux
和一个额外的类型参数,例如
def cartesian[T <: HList](input: T)
(implicit folder: LeftFolder.Aux[T, Seq[HNil], combine.type, _ <: Seq[Any]]) = {
input.foldLeft(Seq[HNil](HNil))(combine)
.foreach(println)
}
现在编译器将知道结果是 Seq[Any]
的某个子类型,因此可以对其调用 foreach
。
当然,这只是里面的问题def
。在呼叫站点,输出类型将根据输入进行解析,因此您可以在没有 Aux
:
的情况下执行此操作
def cartesian2[T <: HList](input: T)
(implicit folder: LeftFolder[T, Seq[HNil], combine.type]) = {
input.foldLeft(Seq[HNil](HNil))(combine)
}
cartesian2(input).foreach(println)
我是 Shapless 的新手。
我正在尝试编写一个函数,它将采用 HList
不同类型的序列,将其转换为包含原始 HList
的笛卡尔积的 Seq[HList]
' s 元素,并遍历生成的 Sequence
例如:
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
来派生一个函数(可能在 class 的情况下),该函数将接受与输入 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
)。无法从传入的函数中推断出来。
问题不是编译器对输入一无所知,而是它对输出.
一无所知在 def cartesian
内部,编译器只知道在 foldLeft
之后你得到某种类型 folder.Out
(这取决于 shapeless 会为你计算的实例)。
为了确保结果类型,您可以使用 LeftFolder.Aux
和一个额外的类型参数,例如
def cartesian[T <: HList](input: T)
(implicit folder: LeftFolder.Aux[T, Seq[HNil], combine.type, _ <: Seq[Any]]) = {
input.foldLeft(Seq[HNil](HNil))(combine)
.foreach(println)
}
现在编译器将知道结果是 Seq[Any]
的某个子类型,因此可以对其调用 foreach
。
当然,这只是里面的问题def
。在呼叫站点,输出类型将根据输入进行解析,因此您可以在没有 Aux
:
def cartesian2[T <: HList](input: T)
(implicit folder: LeftFolder[T, Seq[HNil], combine.type]) = {
input.foldLeft(Seq[HNil](HNil))(combine)
}
cartesian2(input).foreach(println)