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
:
中的Double
s
@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
.
的实现细节
我想在 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
:
Double
s
@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
.