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

如何使用Shapeless的Coproduct创建含多值的Person类型实例?

关于Shapeless Coproduct的误解与正确用法

嘿,先给你澄清一个核心误解:Shapeless的Coproduct(也就是:+:串联CNil的类型)本质是互斥的类型联合——它每次只能容纳其中一个类型的实例,相当于支持多类型选择的Either(Either只能二选一,Coproduct可以多选一)。所以你没法创建同时包含Name、Age和Sex的Person Coproduct实例,这和它的设计目标完全不符。

你现有代码的行为解释

你当前的代码里,Coproduct[Person](Name("Paul"))只存储了Name类型的实例,所以:

  • person.select[Name]会返回Some(Name("Paul"))(找到对应类型的值)
  • person.select[Age]和person.select[Sex]都会返回None(因为当前Coproduct里没有这两种类型的值)

这是Coproduct的正常表现,它本来就是用来表示“多种类型中的某一种”,而非“同时持有多种类型”。

如果要同时持有多个类型的值,该用什么?

如果你需要同时存储Name、Age和Sex,有两种更合适的选择:

1. 使用Shapeless的HList

HList是异质列表,专门用来同时持有不同类型的多个值:

import shapeless._

case class Name(value: String)
case class Age(value: Int)
case class Sex(value: String)

// 定义异质列表类型
type PersonHList = Name :: Age :: Sex :: HNil

@Test def main(): Unit = {
  // 创建同时包含三个类型实例的HList
  val person = Name("Paul") :: Age(30) :: Sex("Male") :: HNil
  
  // 通过类型提取对应的值
  println(person.select[Name]) // 输出 Some(Name(Paul))
  println(person.select[Age])  // 输出 Some(Age(30))
  println(person.select[Sex])  // 输出 Some(Sex(Male))
}

2. 使用普通的Case Class(更直观)

如果不需要Shapeless的泛型编程能力,直接用普通Case Class是最简单的方案:

case class Name(value: String)
case class Age(value: Int)
case class Sex(value: String)

// 直接定义包含所有字段的Case Class
case class Person(name: Name, age: Age, sex: Sex)

@Test def main(): Unit = {
  val person = Person(Name("Paul"), Age(30), Sex("Male"))
  
  println(person.name) // 输出 Name(Paul)
  println(person.age)  // 输出 Age(30)
  println(person.sex)  // 输出 Sex(Male)
}

Coproduct的正确使用场景

Coproduct适合用在“一个值只能是几种类型中的某一种”的场景,比如:

import shapeless._

case class Name(value: String)
case class Age(value: Int)
case class Sex(value: String)

type Person = Name :+: Age :+: Sex :+: CNil

@Test def main(): Unit = {
  // 存储一个Age类型的实例
  val ageOnly = Coproduct[Person](Age(30))
  println(ageOnly.select[Age]) // 输出 Some(Age(30))
  println(ageOnly.select[Name]) // 输出 None

  // 存储一个Sex类型的实例
  val sexOnly = Coproduct[Person](Sex("Male"))
  println(sexOnly.select[Sex]) // 输出 Some(Sex(Male))
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 08:00:07