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

Scala 2中如何通过Macro减少样板代码 自动生成Tuple1到Tuple22隐式方法

Scala 2宏实现Tuple1~Tuple22隐式方法自动生成方案

前置配置(SBT)

首先需要开启Scala 2宏支持,在build.sbt中加入如下配置:

scalaVersion := "2.13.12" // 也支持2.12.x系列版本
libraryDependencies += "org.scala-lang" % "scala-reflect" % scalaVersion.value % Provided
// 2.13.x版本添加该编译选项
scalacOptions += "-Ymacro-annotations"
// 如果是2.12及更早版本,用下面的插件替代上面的编译选项
// addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.1" cross CrossVersion.full)

注意:宏实现代码需要在业务代码之前编译,建议将宏定义放到单独的子模块中,避免单模块下的编译顺序问题。

宏注解实现

自定义宏注解,负责在编译期自动生成1~22的Tuple对应的隐式方法:

import scala.annotation.{StaticAnnotation, compileTimeOnly}
import scala.language.experimental.macros
import scala.reflect.macros.blackbox

@compileTimeOnly("enable macro annotations to expand @GenerateTupleCases")
class GenerateTupleCases extends StaticAnnotation {
  def macroTransform(annottees: Any*): Any = macro GenerateTupleCasesImpl.impl
}

object GenerateTupleCasesImpl {
  def impl(c: blackbox.Context)(annottees: c.Tree*): c.Tree = {
    import c.universe._
    // 批量生成1~22对应的caseTuple方法
    val caseMethods = (1 to 22).map { n =>
      val wildcardParams = List.fill(n)(tq"_")
      val tupleType = tq"Tuple${n}[..$wildcardParams]"
      val methodName = TermName(s"caseTuple${n}")
      
      // Tuple1单独保留原代码的返回类型细化逻辑
      if (n == 1) {
        q"""
          implicit def $methodName[P <: $tupleType](implicit fm: FlatMapper[P, Flat.type]): Flat.Case[P] {
            type Result = FlatMapper[P, Flat.type]#Out
          } = at[P](_.flatMap(Flat))
        """
      } else {
        q"""
          implicit def $methodName[P <: $tupleType](implicit fm: FlatMapper[P, Flat.type]) =
            at[P](_.flatMap(Flat))
        """
      }
    }
    
    // 将生成的方法注入到被注解的对象中
    annottees.head match {
      case q"object $name extends $parent { ..$body }" =>
        q"object $name extends $parent { ..$body ; ..$caseMethods }"
      case _ => c.abort(c.enclosingPosition, "@GenerateTupleCases 只能应用在object上")
    }
  }
}

原业务代码改造

所有重复的样板代码都可以删除,仅需要给Flat对象加上上面定义的宏注解即可:

object TupleFlatten {
  import shapeless._
  import ops.tuple.FlatMapper
  import syntax.std.tuple._

  trait LowPriorityFlat extends Poly1 {
    implicit def default[T] = at[T](Tuple1(_))
  }

  @GenerateTupleCases // 编译时自动生成Tuple1~Tuple22的所有隐式方法
  object Flat extends LowPriorityFlat
}

效果说明

宏生成的代码和手写结构完全一致,无任何运行时开销,编译后字节码和手写版本没有区别,可直接正常使用嵌套Tuple压平功能:

val testTuple = (1, ("2", 3.0), (4L, (true, 5)))
val flattened = testTuple.flatMap(Flat)
// 输出:(1, "2", 3.0, 4L, true, 5)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 15:45:03