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

如何在未知底层数值类型时定义通用算术操作?

问题描述

我正在尝试编写一个接口,允许用户传入与数值类型无关的任意算术函数,随后由辅助代码将其绑定到系统使用的对应底层Numeric类型(需求源于DSL设计,最终数值类型编译时未知,但可限定在小范围内)。

目标是实现类似如下功能(但原代码无法编译):

case class NumericOp(
  intVersion: (Int, Int) => Int,
  floatVersion: (Float, Float) => Float
)
def wrap(f: ???): NumericOp = NumericOp(f[Int], f[Float])

// 使用示例
wrap( (x, y) => x + y )

// 引擎中使用
def applyArithmetic(arg1: Any, arg2: Any, op: NumericOp): Any = {
  (arg1, arg2) match {
    case (a: Int, b: Int) => op.intVersion(a, b)
    case (a: Float, b: Float) => op.floatVersion(a, b)
    case _ => throw new Exception("Invalid types") // 实际不应触发
  }
}

原代码的问题在于,它期望wrap接收未应用类型参数的通用lambda,再将底层类型应用于lambda生成实际函数,但类型参数的应用必须在编译前完成。

核心需求是:先将算术逻辑描述为数据,待类型确定后生成具体实现(比如为Numeric操作设计DSL,后续为Int/Float等生成实现)。希望找到可行方案,同时保持使用端简洁且与底层类型解耦。

明确要求:

  • 用户以类型无关方式指定算术操作,仅需知晓处理Numeric类型,无需了解具体类型或类型数量。
  • 用户的逻辑传入wrap函数,该函数知晓所有可能的底层类型,生成存储类型特定数据的中间结构。
  • 中间结构用于处理动态Any值的解释器中,执行类型检查与转换。

时间流程:

  1. 用户传入算术逻辑至wrap函数。
  2. 系统维护者调整底层数值类型集合,无需告知用户。
  3. 代码编译。
  4. 运行时,具体值以Any类型传入applyArithmetic,同时传入wrap输出的中间结构。
可行解决方案

利用Scala类型类与多态抽象实现

基于Scala的Numeric类型类,可以实现类型无关的算术逻辑描述,同时通过wrap函数自动生成各底层类型的具体实现,无需用户关心细节。

步骤1:定义通用的多态操作类型

先定义一个 trait 描述可适配任意Numeric类型的二元操作:

trait PolyBinaryOp {
  def apply[T: Numeric](a: T, b: T): T
}

步骤2:重新设计中间存储结构

针对固定范围的底层类型,用case class存储各类型的具体实现:

import scala.math.Numeric.Implicits._

case class NumericOp(
  intOp: (Int, Int) => Int,
  floatOp: (Float, Float) => Float
)

步骤3:实现wrap函数

wrap接收PolyBinaryOp,利用Numeric实例自动生成各类型的具体函数:

def wrap(op: PolyBinaryOp): NumericOp = {
  NumericOp(
    intOp = (a, b) => op.apply(a, b),
    floatOp = (a, b) => op.apply(a, b)
  )
}

步骤4:用户端简洁使用

用户只需编写类型无关的算术逻辑,无需关注底层具体类型:

// 加法操作
val addOp = wrap(new PolyBinaryOp {
  override def apply[T: Numeric](a: T, b: T): T = a + b
})

// 乘法操作
val multiplyOp = wrap(new PolyBinaryOp {
  override def apply[T: Numeric](a: T, b: T): T = a * b
})

步骤5:引擎中的类型匹配执行

保持原有的类型匹配逻辑,直接使用生成的NumericOp:

def applyArithmetic(arg1: Any, arg2: Any, op: NumericOp): Any = {
  (arg1, arg2) match {
    case (a: Int, b: Int) => op.intOp(a, b)
    case (a: Float, b: Float) => op.floatOp(a, b)
    case _ => throw new IllegalArgumentException("不支持的数值类型")
  }
}

扩展支持更多数值类型

如果后续需要新增底层类型(比如Double),只需修改NumericOp和wrap函数,用户代码无需改动:

case class NumericOp(
  intOp: (Int, Int) => Int,
  floatOp: (Float, Float) => Float,
  doubleOp: (Double, Double) => Double // 新增Double版本
)

def wrap(op: PolyBinaryOp): NumericOp = {
  NumericOp(
    intOp = (a, b) => op.apply(a, b),
    floatOp = (a, b) => op.apply(a, b),
    doubleOp = (a, b) => op.apply(a, b) // 自动生成Double实现
  )
}

进一步简化用户写法

通过隐式转换,让用户可以直接传入lambda:

// 定义隐式转换,将类型无关的二元函数转为PolyBinaryOp
implicit def funcToPolyOp[T](f: (T, T) => T): PolyBinaryOp = new PolyBinaryOp {
  override def apply[U: Numeric](a: U, b: U): U = f(a, b)
}

// 用户直接传入算术逻辑lambda
val addOp = wrap((x, y) => x + y)
val subtractOp = wrap((x, y) => x - y)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 16:25:28