You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Shapeless入门:如何将Seq构成的HList转换为其笛卡尔积的Seq[HList]

嘿,刚上手Shapeless确实得适应一下它的类型驱动风格,不过你要的这个“Seq组成的HList转笛卡尔积Seq[HList]”的需求,用Shapeless的类型类来实现刚好合适,咱们一步步来写:

第一步:引入Shapeless依赖

首先得确保你的项目里加了Shapeless的依赖,比如sbt项目里:

libraryDependencies += "com.chuusai" %% "shapeless" % "2.3.10"

第二步:定义笛卡尔积的类型类

Shapeless的核心是用类型类来处理不同的HList类型,我们先定义一个CartesianProduct类型类,负责把输入的HList(每个元素是Seq)转换成对应的笛卡尔积Seq[HList]:

import shapeless._

trait CartesianProduct[L <: HList] {
  // 定义输出HList的类型
  type Out <: HList
  // 实现转换逻辑
  def apply(l: L): Seq[Out]
}

object CartesianProduct {
  // 用Aux类型别名简化类型参数的传递,让implicit实例的类型更清晰
  type Aux[L <: HList, O <: HList] = CartesianProduct[L] { type Out = O }

  // 处理空HNil的情况:空的笛卡尔积就是包含HNil的Seq
  implicit val hnilCartesian: Aux[HNil, HNil] = new CartesianProduct[HNil] {
    type Out = HNil
    def apply(l: HNil): Seq[HNil] = Seq(HNil)
  }

  // 处理非空的Cons类型(即Seq[B] :: Tail)
  // 递归地把第一个Seq的每个元素,和Tail部分的笛卡尔积结果拼接
  implicit def consCartesian[B, Tail <: HList, TailOut <: HList](
    implicit tailCp: Aux[Tail, TailOut]
  ): Aux[Seq[B] :: Tail, B :: TailOut] = new CartesianProduct[Seq[B] :: Tail] {
    type Out = B :: TailOut
    def apply(l: Seq[B] :: Tail): Seq[Out] =
      l.head.flatMap(b => tailCp(l.tail).map(b :: _))
  }
}

第三步:封装调用函数

为了方便调用,我们写一个顶层函数,让Scala的implicit机制自动帮我们找到对应的类型类实例:

def cartesianProduct[L <: HList](l: L)(implicit cp: CartesianProduct[L]): Seq[cp.Out] = cp(l)

第四步:示例使用与遍历

现在就可以用你给的输入来测试了,还可以把HList转成Tuple方便打印:

object CartesianProductExample extends App {
  // 你的输入HList
  val input = Seq(true, false) :: Seq(1,2,3) :: Seq("foo", "bar") :: HNil
  
  // 生成笛卡尔积
  val productSeq = cartesianProduct(input)

  // 遍历并打印结果
  println("生成的笛卡尔积:")
  productSeq.foreach { hlist =>
    // 用Shapeless的tupled方法把HList转成普通Tuple
    import shapeless.syntax.std.tuple._
    println(hlist.tupled)
  }
}

运行这个程序,你会得到类似这样的输出:

生成的笛卡尔积:
(true,1,foo)
(true,1,bar)
(true,2,foo)
(true,2,bar)
(true,3,foo)
(true,3,bar)
(false,1,foo)
(false,1,bar)
(false,2,foo)
(false,2,bar)
(false,3,foo)
(false,3,bar)

简单解释一下逻辑

  • 类型类CartesianProduct是核心,它为不同的HList类型定义了转换规则:
    • 当输入是HNil时,返回只包含HNil的Seq,这是递归的终止条件;
    • 当输入是Seq[B] :: Tail时,先递归处理Tail得到它的笛卡尔积,然后把Seq[B]里的每个元素和Tail的每个结果拼接,用flatMap和map组合实现。
  • Aux类型别名是Shapeless里的常用技巧,用来把类型类的关联类型(这里的Out)暴露成显式的类型参数,方便implicit实例的定义和使用。

内容的提问来源于stack exchange,提问作者dkarlinsky

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 07:41:36