ew is very useful, but only in very limited circumstances. It is primarily intended for library authors writing internal code that will not be exposed to end users, with the public API made safe and reasonable by nothing but the authors' understanding and diligence.
"com.raquo" %%% "ew" % "<version>" // Scala.js
js.Array as if it's part of Scala collections library.
js.Array contains instances of a Scala case class? The browser engine knows nothing about Scala's
equals method and the case classes' structural equality semantics, so on its own it would produce results that don't always make sense in Scala.
Well, that wouldn't cut it for us, Scala connoisseurs, would it? And so Scala.js takes care of this behind the scenes. You can find Scala.js' implementation of
indexOf method in their ArrayOps file. As you can see it has to iterate over the array to apply Scala equality checks, and that has no chance of being as fast as the browser engine's C++ implementation of
Despite the inefficiency, this works just fine for most users most of the time. Nothing is ever free, and correctness-over-performance is usually a good default tradeoff.
ew is for the poor souls who need to juice more performance out of JS data structures. When using ew, you're only calling raw JS methods (or a faster equivalent, if available). You get code that's as fast as possible, but you pay for that with increased cognitive load and general annoyance.
Also: Vector – a JS Array in an immutable trenchcoat.
The interfaces defined in ew follow the corresponding native JS types very closely. We keep JS names for methods even if they go against Scala naming conventions (so we have
Array.foreach). Since such methods lack Scala semantics, we don't actually want to make them look at home in Scala.
See comments in the code for any details. Don't expect a comprehensive explanation of JS semantics, it's something you need to know (or test) yourself. See also how this library is used in Airstream and Laminar.
com.raquo.ew._ to get all types and implicits. Convert Scala types to ew types using
ew extension methods (or factories like
JsArray.from(scalaJsArray)), and use the
asScalaJs methods on
ew types to go back. These conversions are zero-cost at runtime, they're just
Some native JS methods have limited browser compatibility (mostly just a problem with Internet Explorer). Scaladoc should say when no version of IE supports a given method, otherwise look it up on MDN: Array, Set, Map.
Note that some Scala.js methods are not supported by all browsers either. For example,
js.Set.foreach is implemented using JS iterators, but no version of IE supports those.
Scala.js attempts to preserve Scala semantics for JS types, but ew does not. The main concern are equality checks: native JS types use only reference equality to compare objects, they're not aware of Scala's
equals method or case classes' structural equality. Aside from the obvious effect on methods like
indexOf, this also affects the uniqueness checks of
Set values and
Note that Scala.js itself does not always wrap JS types with Scala semantics. For example,
js.Array#contains does perform Scala equality checks, whereas
js.Map#contains does not, because that's not really feasible. Of course in practice that's not a huge deal – Scala has proper Maps in its own collections library, so
js.Map is used mostly for interop with JS libraries where JS values are expected (and Scala semantics are not needed).
Some Scala.js methods are actually faster than native JS methods. For example,
js.Array.foreach appears to be faster than native JS
Array.forEach method (at least when called from Scala.js with a
Array.forEach method? Because of this, ew's
JsArray.forEach method does not call into native JS forEach method, but has a loop-based implementation too. The only purpose of this library is performance, so we reserve the right to do such ugly things where it makes sense.
I did not check every method's performance, only the ones that I was personally bottlenecked by –
forEach for all types. I don't expect performance problems with other methods.
Basically, ew defaults to native JS implementation, unless a different implementation is known to offer an equivalent API with superior performance. It would be nice to set up proper benchmarks for every method, but I don't have time for that. I have very rudimentary benchmarks in my laminar-examples repo (see MiscBenchmarks file). Contributions of a better test setup are welcome!
No time to write changelog for this project, sorry. Some notable breaking changes will be announced in Laminar release blog posts, but otherwise you'll need to rely on the Scala compiler to tell you what's wrong.
Versions 0.N.0 and 0.(N+1).0 are not supposed to be compatible.
- Scala.js semantics – Differences in behaviour from Scala on JVM
- Cross-Platform Language Design – Sébastien's Scala.js PhD thesis
- MDN Reference – Frontend developer's best friend
Nikita Gazarov – @raquo
Please sponsor my open source work to make it more sustainable.
ew is provided under the MIT license.
ew type definitions were originally derived from Scala.js code licensed under the Apache 2.0 License.
Comments that borrow from or link to MDN are derived from content created by Mozilla Contributors and are licensed under Creative Commons Attribution-ShareAlike license (CC-BY-SA), v2.5.