如何使用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
相关产品推荐
相关产品推荐

