You need to enable JavaScript to run this app. Shapeless 中 Product 类 Option 转全 Option 元素 HList 实现咨询
优惠活动
大模型
产品
解决方案
定价
更多

Shapeless 中 Product 类 Option 转全 Option 元素 HList 实现咨询

基于Shapeless实现可选case class的统一Option化HList生成

实现思路

None分支的核心需求是:在无case class实例的前提下,生成与Mapper转换后类型完全一致、所有元素为None的Option化HList。由于EnsureOptional的转换规则决定了输出HList的所有元素必然是Option类型,只需为这类全Option元素的HList定义可自动推导的全None实例生成类型类,即可补全None分支的逻辑。

完整代码实现

  • 定义全None HList生成类型类
import shapeless._
import shapeless.ops.hlist.Mapper

trait AllNone[L <: HList] {
  def get: L
}

object AllNone {
  // HNil 边界实例
  implicit val hnilAllNone: AllNone[HNil] = new AllNone[HNil] {
    override val get: HNil = HNil
  }

  // 递归实例:处理头元素为Option[H]、尾为满足约束的HList的结构
  implicit def hconsAllNone[H, T <: HList](implicit tailAllNone: AllNone[T]): AllNone[Option[H] :: T] = 
    new AllNone[Option[H] :: T] {
      override val get: Option[H] :: T = None :: tailAllNone.get
    }
}
  • 精简EnsureOptional转换规则(移除冗余的HNil case,Mapper仅对HList元素生效,无需处理HNil本身)
trait LowPriEnsureOptional extends Poly1 {
  // 低优先级规则:非Option输入,包装为带值的Option
  implicit def wrapNonOption[In]: Case.Aux[In, Option[In]] = at(thing => Some(thing))
}

object EnsureOptional extends LowPriEnsureOptional {
  // 高优先级规则:输入本身为Option类型,直接保留
  implicit def keepOption[In <: Option[_]]: Case.Aux[In, In] = at(identity)
}
  • 补全None分支逻辑
object OptionizeHlist {
  def apply[CC <: Product, R <: HList, OptionalR <: HList](occ: Option[CC])(
    implicit
    gen: Generic[CC] { type Repr = R },
    mapper: Mapper.Aux[EnsureOptional.type, R, OptionalR],
    allNone: AllNone[OptionalR]
  ): OptionalR = occ match {
    case Some(cc) => mapper(gen.to(cc))
    case None     => allNone.get
  }
}

效果验证

针对示例中的Foo类,直接调用即可得到符合预期的输出:

case class Foo(a: String, b: Option[Long], c: Option[String])

// Some分支:生成带具体值的Option化HList
OptionizeHlist(Some(Foo("abc", Some(123L), None)))
// 输出: Some("abc") :: Some(123L) :: None :: HNil

// None分支:无实例时生成同类型全None HList
OptionizeHlist[Foo](None)
// 输出: None :: None :: None :: HNil

两个分支的返回类型完全一致,均为Option[String] :: Option[Long] :: Option[String] :: HNil。

适配性说明

  • 整个流程的实例解析完全依赖Shapeless的隐式推导能力,不需要为每个case class手动编写实现
  • EnsureOptional的优先级规则保证非Option元素被正确包装,原生Option元素不会被嵌套成Option[Option[_]]
  • AllNone的递归结构与HList结构一一匹配,只要Mapper输出的元素全为Option,即可自动推导出全None实例,不需要额外的类型参数

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.11.03 16:24:08