如何在未知底层数值类型时定义通用算术操作?
问题描述
我正在尝试编写一个接口,允许用户传入与数值类型无关的任意算术函数,随后由辅助代码将其绑定到系统使用的对应底层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值的解释器中,执行类型检查与转换。
时间流程:
- 用户传入算术逻辑至
wrap函数。 - 系统维护者调整底层数值类型集合,无需告知用户。
- 代码编译。
- 运行时,具体值以
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
相关产品推荐
相关产品推荐

