You need to enable JavaScript to run this app. Scala 3宏实现ToExpr[Foo]时如何构造Expr[Int => Int]实例
优惠活动
大模型
产品
解决方案
定价
更多

Scala 3宏实现ToExpr[Foo]时如何构造Expr[Int => Int]实例

错误原因分析

你两次尝试的报错本质都是Scala 3多阶段编程的层级规则限制:

  • 尝试1报错:'{ ... }包裹的引用块属于 staging level 1,而你匹配到的f是当前运行上下文(staging level 0)的普通值,跨层级直接访问被语法禁止。
  • 尝试2报错:$ splice操作符只能在引用块内插入Expr[_]类型的值,你传入的f是普通的Int => Int函数,类型不匹配。

解决方案

要实现ToExpr[Foo],核心是将level 0的普通函数值转换为Expr[Int => Int],提供两种可行方案:

方案1:通用序列化方案(支持任意可序列化函数)

Scala的函数字面量默认实现了序列化接口,只要捕获的变量都可序列化,就可以通过序列化+反序列化的方式完成lift,代码如下:

import scala.quoted.*
import java.io.*

case class Foo(f: Int => Int)

given ToExpr[Foo] with
  def apply(foo: Foo)(using Quotes): Expr[Foo] =
    // 序列化level 0的函数为字节数组
    val byteOut = ByteArrayOutputStream()
    val objOut = ObjectOutputStream(byteOut)
    objOut.writeObject(foo.f)
    objOut.close()
    val funcBytes = byteOut.toByteArray

    // 利用标准库内置的Array[Byte] ToExpr实例lift字节数组,再在引用块内反序列化得到函数
    val exprF = '{
      val byteIn = ByteArrayInputStream(${Expr(funcBytes)})
      val objIn = ObjectInputStream(byteIn)
      objIn.readObject().asInstanceOf[Int => Int]
    }

    '{Foo($exprF)}

该方案无需额外适配,适用于大部分业务场景。

方案2:静态函数专属方案(性能更高)

如果你用到的Int => Int函数都是编译时已知的固定逻辑,可以手动实现ToExpr[Int => Int]实例避免序列化开销,示例如下:

import scala.quoted.*

case class Foo(f: Int => Int)

// 自定义Int=>Int的ToExpr实例,仅适配你用到的函数逻辑
given ToExpr[Int => Int] with
  def apply(f: Int => Int)(using Quotes): Expr[Int => Int] = f match
    // 匹配加1函数
    case _ if f(0) == 1 && f(100) == 101 => '{ (i: Int) => i + 1 }
    // 匹配乘2函数
    case _ if f(0) == 0 && f(100) == 200 => '{ (i: Int) => i * 2 }
    // 其他你需要的函数匹配分支

given ToExpr[Foo] with
  def apply(foo: Foo)(using Quotes): Expr[Foo] =
    val exprF = Expr(foo.f) // 直接调用上面自定义的ToExpr实例
    '{Foo($exprF)}

注意事项

  • 序列化方案要求函数及其捕获的所有变量都实现Serializable接口,Scala原生函数字面量默认满足该要求。
  • 静态函数方案性能更高,但只能处理预设范围内的函数逻辑,需要根据业务场景选择。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.17 03:36:06