Scala如何实现函数入参任意嵌套Tuple的通用扁平化转换?
解决方案
依赖说明
首先需要在项目中引入shapeless依赖(以sbt为例):
libraryDependencies += "com.chuusai" %% "shapeless" % "2.3.10"
完整实现代码
import shapeless._ import ops.tuple.FlatMapper trait LowPriorityFlatten extends Poly1 { implicit def default[T] = at[T](Tuple1(_)) } object flatten extends LowPriorityFlatten { implicit def caseTuple[P <: Product](implicit lfm: Lazy[FlatMapper[P, flatten.type]]) = at[P](lfm.value(_)) } // 泛化的ft函数,无手动类型转换,编译期保证类型安全 def ft[Nested <: Product, Flat <: Product, Res](f: Flat => Res)( implicit fm: FlatMapper[Nested, flatten.type] { type Out = Flat } ): Nested => Res = (nested: Nested) => f(fm(nested))
测试用例
测试场景1:适配(Int, (Int, Int))嵌套Tuple
val f0: ((Int, Int, Int)) => Int = x => x._1 + x._2 + x._3 def g(f: ((Int, (Int, Int))) => Int): Int = f(1,(2,3)) // 直接调用,无需手动转换类型 println(g(ft(f0))) // 输出 6
测试场景2:适配((Int, Int), (Int, Int))嵌套Tuple
val f1: ((Int, Int, Int, Int)) => Int = x => x._1 + x._2 + x._3 + x._4 def g1(f: (((Int, Int), (Int, Int))) => Int): Int = f((1,2), (3,4)) println(g1(ft(f1))) // 输出 10
原理解释
- 原代码需要手动强转的核心原因是没有将
FlatMapper的输出类型和目标扁平Tuple类型绑定,编译器无法推导flatten(p)的具体类型。 - 通过给隐式参数
fm添加类型细化{ type Out = Flat },建立了嵌套Tuple类型Nested扁平化后的结果一定是Flat类型的编译期约束,所有类型校验都在编译阶段完成,完全避免运行期类型转换的风险。 - 该实现支持任意层数、任意结构的Tuple嵌套转换,不需要针对每个场景单独编写适配逻辑。
内容的提问来源于stack exchange,提问作者counter2015
相关产品推荐
相关产品推荐

