Looking into Scala.js
Introduction
This blog post was created to see how Scala.js is holding on and having some fun with programming. I decided to do a simple mini-game using Scala.js and Three.js libraries.
I will explore Scala.js from a practical perspective. You’ll see the perks of using typed languages in frontend. We are also going to take a look under the hood and see some generated JS code. Finally, I’m going to present some advantages and disadvantages of Scala.js and try to answer the question: Is Scala.js production ready?
I encourage you to read this blog post about Scala.js basics first, but it’s not required.
My inspiration for the game was: https://oos.moxiecode.com/js_webgl/xwing/
Source code of my game: https://github.com/andrjew/scalac-fighter/tree/0.1.1
Why Three.js library? I chose it because I’m familiar with it and I knew that it’ll provide me things I need. Three.js is in fact 3D library and initially I also wanted to create a 3D mini game, but it ended up being 2D ;)
The game
This is how the game looks like:
And here you can have some fun and play: https://fighter.scalac.io
I know the game is far from perfection. I’ve stopped developing it when the game became playable. That leave much place for improvements. If you want to add some features to the game or just see how it is to program in Scala.js, just grab the code from Github: https://github.com/andrjew/scalac-fighter.
About the project
We had our fun time, now let’s see how the game looks from the inside.
Firstly please see project structure to understand basic concept:
I believe architecture here is not too hard. Every class is quite self-explanatory. The most of game’s logic is located inside Game class.
One thing, which I want to notice is the force of having typed language like Scala. The advantage over JS is that I’m unable to accidentally confuse Bullet
with EnemyShip
. In JavaScript such mistake is highly possible and can be hard to debug. In Scala, the type system protects me form this kind of misery.
Moreover, please look at this example from Game.scala:
This code is responsible for moving objects on each animation frame. But I only want to move object that are self-moving (like bullet), so I created a special SelfMoving trait. As a result I can use pattern matching on game’s objects collection and easily move only self-moving items.
The Type System Strikes Back
Another example will be from Main.scala:
This piece’s task is to react on player’s mouse movements. For capturing mouse movements I’ve used the jQuery library (wrapper). We can see that Scala.js allows us to use anonymous function just like in Scala.
But something more intriguing is happening here. Our anonymous function take parameter of type JQueryEventObject
and unfortunately this type doesn’t have event.clientX
field, which I need to move player’s ship. This happens because jQuery event is normalized into jQuery abstraction (see this for details). The funny thing is that the field clientX
is there (we can observe it while debugging JS), but we can’t see it, because we’re limited with JQueryEventObject
trait.
Sadly attempt of using partial function:
won’t succeed, there is an compile error: The argument types of an anonymous function must be fully known. (SLS 8.5)
.
Neither below mixing works:
The parameter type must be exactly JQueryEventObject
class or it’s ancestor.
And not surprisingly pattern matching also fails:
This fails because proposed case will never be matched.
All of our Scala forces has failed. We need to use the dark side of the Force:
Solution is to cast event: JQueryEventObject
object to DOM’s standard MouseEvent
which is represented in Scala.js under org.scalajs.dom.MouseEvent
class. This works because in JS runtime this object is in a fact an MouseEvent
.
Maybe some persons sense some more disturbance here. If yes, then you’re right. The dark side reveals again:
One of the most terrifying power of the dark side, the mutability attacks!
The mutable trap
Please see following example from GameScene class:
As you can observe this class is not written in immutable style. Adding a new object to GameScene doesn’t create new instance of itself. Instead of that it is changing it’s inner state. Yes, I know. We don’t like mutability, but there is a cause of this state.
Please notice that scene
is instance of threejs.Scene
. This scala class is just a direct wrapper for Three.js (JavaScript) implementation. Because of that you can look at scene.add(gameObject.object3d)
as it was pure JS code, which you can’t change. Unfortunately JS implementation of function scene.add(...)
is mutable and this is something I had to accept.
Three.js library was designed in mutable way and this is not a mistake or bad thing. The problem occurs when we want to write our application in immutable/functional way as we used to do in Scala.
Unfortunately when using a Three.js under the hood we are not able to achieve this easily.
Image a situation when you want to do a fully immutable copy of my game. You would have to create each frame of animation (each object) from zero. This is possible, but performance impact would be probably too big. Please notice that creating a new object in javascript can absorb a way more time than using setter.
It’s possible to fight JS libraries mutability, but when taking Scala.js (in immutable style) into consideration always remember about possible extra work overhead and performance issues.
With current state of JavaScript itself and it’s libraries, personally I thing fighting with mutability is pointless. For me it’s something you will need to accept when deciding to use Scala.js. If you can’t beat them, join them.
Where is JS ?
In this paragraph I want to make you aware of what happening after compiling Scala code to JavaScript. So let’s see where we can find generated JS code and how does it looks like after compilation.
After compiling to javascript with sbt fastOptJS
command, you can find output files in scalac-fighter/target/scala-2.11
directory. The most important file there is scalac-fighter-fastopt.js
. This file includes compiled game code and also Scala.js related code. That’s making the file 17 783 lines long :(
This is unnecessary obstacle and makes reading compiled code even harder. I’m aware that not every person will explore gendered code, but some compiler option providing my code and Scala.js separately would be great.
Let’s take a quick look into some example:
Scala:
JavaScript (formatted by me):
As we can see the generated code is not very eye-friendly. Function name in JS is 5 times longer than in Scala.
Probably __s_Option
fragment is responsible for noticing scala’s Option
here, but this is only my hunch.
Next part,org_denigma_threejs_Vector3
is just fully qualified class name for Three.js object.
Quite mysterious function is $uD
, but we can find it in generated code and this is:
So mystery is solved ;] this is just a function that ensure Double type. Further we see how Scala.js compiler handle Scala’s Option
by using simple if-else
plus isEmpty__Z
function.
Should I care about generated JS ?
Generally I think not, but it’s good to know what’s going on ;]
Even tough reading compiled Scala.js code is hard, looking into it will make You better understand how Scala.js works and what limitations it could have.
Conclusions
Things I like:
- Easy to start working with
- Many, ready to use JS libraries facades for Scala.js. I was very positively surprised to find a Three.js facade
- Good alternative for person who must do something in JS, but can’t look at it :)
- You can debug Scala code directly from browser. This really works and it’s great! This is possible thanks to sourcemap mechanism. To try this just open developer tools in browser and then in source section debug Scala code like you normally debug JS code.
- Of course type system (safety, clarity and other)
- Support for autocomplete and other perks of IDE
Things I dislike:
- You can’t escape from mutable JS libraries
- Generated code is rather hard to read (but not impossible)
- Possible more inconveniences like the one with
JQueryEventObject
andMouseEvent
Can I use Scala.js in production ?
Probably this is the most important question. Is it production ready ?
Unfortunately I’m not going to give you an easy answer. More proper question is: Does Scala.js do the things that I require? Does it complete my requirements ?.
As for me, Scala.js worked fine in my mini project. I’ve created working game in language I like with no bigger problems. It was really pleasant and fun experience. Personally I feel very positive about this technology. I believe that it’s no longer just an experiment, but aspire to be an alternative for JavaScript, especially for persons from backend world.
In summary my answer won’t be just Yes or No. Instead of that I will provide a checklist, that will help you to know if Scala.js is something you can use.
Checklist (before using in production):
- Absolutely MUST do a recognition by yourself.
- Learn how Scala.js works, what are the differences between Scala and Scala.js.
- Read docs(Scala.js pages) and other articles(Hands-onScala.js)
- Try Scala.js in applications / projects:
– write some mini project
– try to break/hack Scala.js
– take my project and just have some fun with it ;)
- Check availability of Scala.js facades for JavaScript libraries you are going to use. Many people do Scala.js wrappers for most popular libraries. If however you can’t find wrapper, you’ll have to do one by yourself. See more details here.
- Look into generated JS code to be aware what’s really going on.
Credits
Thanks to:
- Scala.js team. I want to say that I admire you for work that they already done. Combining these two languages is not easy for sure. Keep fighting the good fight.
- Three.js team – I’m enthusiast of this library for quite long time.
- Creator of facade for Three.js library: https://github.com/antonkulaga/threejs-facade.
- Patryk Jażdżewski for help with project and game code.
May the force be with you :)
Do you like this post? Want to stay updated? Follow us on Twitter or subscribe to our Feed.
See also