Is it possilble to a create type-safe library in S

2019-09-18 02:08发布

问题:

If so how? In my last question I asked why this snippet is crashing:

@JSExport("Test") object TestScalaJs {
  @JSExport def callMe[A: ClassTag](f: js.Function1[Int, A]): Array[A] = Array(f(4))
}

on:

var res = Test().callMe(function(x) {return x + 'x';});
console.log('TestScalaJs:', res.join(', ')); 

with:

$c_jl_Throwable.fillInStackTrace__jl_Throwable  @   VM153:4161
$c_sjsr_UndefinedBehaviorError.fillInStackTrace__jl_Throwable   @   VM153:9241
$c_jl_Throwable.init___T__jl_Throwable  @   VM153:4177
$c_sjsr_UndefinedBehaviorError.init___T__jl_Throwable   @   VM153:9248
$c_sjsr_UndefinedBehaviorError.init___jl_Throwable  @   VM153:9244
$throwClassCastException    @   VM153:196
$as_s_reflect_ClassTag  @   VM153:7824
$c_LTestScalaJs$.callMe @   VM153:2061
(anonymous function)    @   VM155:2
$s_Lscalatags_jsdom_Frag$class__applyTo__Lscalatags_jsdom_Frag__Lorg_scalajs_dom_raw_Element__V @   VM153:1108
$c_Lscalatags_JsDom$TypedTag.applyTo__O__V  @   VM153:9981
$c_Lfiddle_Fiddle$.println__sc_Seq__V   @   VM153:2113
$c_LScalaFiddle$.init___    @   VM153:2021
$m_LScalaFiddle$    @   VM153:2034
(anonymous function)    @   VM154:1
(anonymous function)    @   VM77 resultframe?theme=light:32

And the response was that it is because I am missing implicit argument for callMe. I didn't think of that before because, well, Scala is providing it auto-magically, so this just worked on a first try:

object TestVanilla {
  def callMe[A: ClassTag](f: Int => A): Array[A] = Array(f(4))
}

val res = TestVanilla.callMe(_ + "x");
println(res.mkString(", "));

The question is:

How one is supposed to be writing type-safe (at least in a compilation time, no need for dynamic checking) libraries using ordinary features of Scala like integers, arrays, functions, generics and ClassTags in ScalaJS which can be then easily* used from JavaScript? I really hope it is possible.

*: I mean like no arcane requirements such as manual construction of ClassTags from JavaScript.

回答1:

You probably want to use a js.Array in your example:

@JSExport("Test") object TestScalaJs {
  @JSExport def callMe[A](f: js.Function1[Int, A]): js.Array[A] = js.Array(f(4))
}

Array is a Scala / Java style array and opaque from JavaScript (i.e. doing anything on it is undefined behavior). A js.Array is a JavaScript array that you can actually use from JavaScript. Like most Scala collections, it does not need a ClassTag to be constructed.