【问题标题】:Taking HList of Seq[_] and generating Seq[HList] with cartesian product of values获取 Seq[_] 的 HList 并使用值的笛卡尔积生成 Seq[HList]
【发布时间】: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) =&gt; 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")) 

请注意,函数参数abc 的类型被正确推断为BooleanStringInt。 目前,传递给foreach 的函数的结果类型必须是固定的(在上面的代码中是Unit)。无法从传入的函数中推断出来。

【问题讨论】:

  • 你能提供你正在使用的进口吗?
  • 当然:import shapeless._ import shapeless.ops.hlist.LeftFolder

标签: scala shapeless


【解决方案1】:

问题不在于编译器对输入一无所知,而是它对输出一无所知。

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)

可运行代码:https://scalafiddle.io/sf/n409yNW/2

【讨论】:

  • 太好了,谢谢!你知道在函数内部使foldLeft 的结果类型为Seq[Boolean::Int::String::HNil] (例如)的方法,就像我在函数外部进行折叠时一样(@987654334 的完整类型@ 已知)?
  • 好吧,我想我明白了。如果我添加一个类型参数[R &lt;: HList] 并将_ &lt;: Seq[R] 而不是_ &lt;: Seq[Any] 传递给LeftFolder.Aux,则结果类型将完全解析!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-10-08
  • 2021-05-06
  • 1970-01-01
  • 2021-09-05
  • 2020-08-24
  • 2016-05-07
  • 1970-01-01
相关资源
最近更新 更多