Scala.js 中的数字隐含

Numeric implicity in Scala.js

我想在 scala.js 中使用 Numeric。我需要在某些函数中使用数字隐式(我添加 scala.js 的库已经有了)。例如此代码:

import scala.scalajs.js.annotation.{JSExport, JSExportTopLevel}

@JSExportTopLevel("NumericOps")
class JsNumericOps[V] {
  @JSExport("add")
  def add(x: V, y: V)(implicit num: Numeric[V]): V = {
    num.plus(x, y)
  }
}

class NumericOps[V: Numeric] {
  def add(x: V, y: V)(implicit num: Numeric[V]): V = {
    num.plus(x, y)
  }
}

之后我 运行 它在 javascript 控制台中:

NumericOps().add(3,3)

并得到错误:

scalajsenv.js:211 Uncaught $c_sjsr_UndefinedBehaviorError {s: "An undefined behavior was detected: undefined is not an instance of scala.math.Numeric", e: $c_jl_ClassCastException, stackTrace: null, stackdata: $c_sjsr_UndefinedBehaviorError, stack: "Error↵ at $c_sjsr_UndefinedBehaviorError.$c_jl_…ndpit-fastopt.js:1188:15)↵ at :1:14"}

在 scala.js 中使用 Numeric[T] 隐式的正确方法是什么?

如果您不与 JavaScript 互操作,您对 implicit Numeric[T] 的使用是有意义的。在 Scala 代码中,隐含的工作方式与 Scala 中相同。但是当与JavaScript互操作时,隐式参数变成完全显式:JavaScript没有带有隐式解析的类型检查器来填充缺失的参数,所以即使是隐式参数需要在JS中的调用站点提供。

显然,这不是您想要的。事实上,除非您导出对 Numeric 相关实例的引用,否则 JavaScript 代码甚至无法将它们作为参数传递。

相反,您只需要导出不需要 Numeric[T] 参数的方法,这意味着您需要 重载 您的方法 add所有可能的 JS 数字类型。不过,你在这里很幸运:JS 只有 one number 类型,它在 Scala.js 中转换为 Double。因此,你需要做的就是只处理JsNumericOps:

中的Doubles
@JSExportTopLevel("NumericOps")
class JsNumericOps {
  @JSExport
  def add(x: Double, y: Double): Double = {
    x + y
  }
}

同时保持 NumericOps 通用和相关的 Numeric 隐式。

如果您想在 JsNumbericOps 中重用 NumericOps 的实现,您可以很容易地这样做:

@JSExportTopLevel("NumericOps")
class JsNumericOps {
  private val numericOps = new NumericOps

  @JSExport
  def add(x: Double, y: Double): Double = {
    numericOps.add(x, y)
  }
}

在这里,Scala 编译器会在JsNumericOps.add 内推断出需要给numericOps.add(x, y)implicit Numeric[Double]。 JavaScript 代码永远不需要看到它,这是 JsNumericOps.add.

的实现细节