I'm trying to export a Scala implementation of an algorithm for use in JavaScript. I'm using @JSExport
. The algorithm works with Scala Char
and Long
values which are marked as opaque in the interoperability guide.
I'd like to know (a) what this means; and (b) what the recommendation is for dealing with this.
I presume it means I should avoid Char
and Long
and work with String
plus a run-time check on length (or perhaps use a shapeless Sized
collection) and Int
instead.
But other ideas welcome.
More detail...
The kind of code I'm looking at is:
@JSExport("Foo")
class Foo(val x: Int) {
@JSExport("add")
def add(n: Int): Int = x+n
}
...which works just as expected: new Foo(1).add(2)
produces 3
.
Replacing the types with Long
the same call reports:
java.lang.ClassCastException: 1 is not an instance of scala.scalajs.runtime.RuntimeLong
(and something similar with methods that take and return Char
).
In computer science, an opaque data type is a data type whose concrete data structure is not defined in an interface. This enforces information hiding, since its values can only be manipulated by calling subroutines that have access to the missing information.
Opaque types are a kind of type alias. They are called opaque because, unlike an ordinary type alias, most Rust code (e.g., the callers of as_u32s ) doesn't know what type AsU32sReturn represents. It only knows what traits that type implements (e.g., IntoIterator<Item = u32> ).
An opaque type refers to one specific type even though the caller of the function can't see the type, while a protocol type can refer to any type that conforms to the protocol.
The opaque type alias is introduced in Scala 3 to provide type abstraction without any overhead to solve the above-mentioned issues.
Being opaque means that
@JSExport
ed constructor)@JSExport
ed methods and fields)It is still possible to receive a value of that type from Scala.js code, pass it around, and give it back to Scala.js code. It is also always possible to call .toString()
, because java.lang.Object.toString()
is @JSExport
ed. Besides toString()
, neither Char
nor Long
export anything, so you can't do anything else with them.
Hence, as you have experienced, a JavaScript 1
cannot be used as a Scala.js Long
, because it's not of the right type. Neither is 'a'
a valid Char
(but it's a valid String
).
Therefore, as you have inferred yourself, you must indeed avoid opaque types, and use other types instead if you need to create/manipulate them from JavaScript. The Scala.js side can convert back and forth using the standard tools in the language, such as someChar.toInt
and someInt.toChar
.
The choice of which type is best depends on your application. For Char
, it could be Int
or String
. For Long
, it could be String
, a pair of Int
s, or possibly even Double
if the possible values never use more than 52 bits of precision.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With