Scala.jsとは
いわゆるAltJSです。Scalaで書いて、JSに書き出されます。
詳細は、公式サイトをみてください。
ScalaJS対応のライブラリは結構たくさんあります。こちらから探してみてください。
JVM/JS双方に対応したシリアライズ系のライブラリなど、実運用に必要なライブラリは揃っているのではないかと思います。
また、JSで書かれたライブラリに対するFacade(Scala.jsから利用出来るようにするつなぎの部分)のライブラリも色々と出ています。
Scalaが好き、Scalaで書きたい!というScala愛に溢れる方にはもってこいなソリューションではないでしょうか。
...が、どんなものにも、良い面があれば、そうでない面もあります。
以下、僕の考えるScala.jsのメリット/デメリットを列挙し、そこから適した/適さないユースケースを考察してみます。
Scala.jsのデメリット
まず僕がデメリットと思うことから挙げてみます。
(合わせて、各デメリットに対するちょっとした対策のアイデアも書いていきます)
プロジェクト構成が複雑化
後述しますが、Scala.jsのプロジェクトはJVM側とコードを共有することでシナジー効果が生まれるので、基本的にはクロスプロジェクト構成を取ることになると思います。
...が、この設定が初見だとなかなかに大変です。
サーバ/クライアント(ブラウザ)の構成で使う場合、Playやakka-http、その他の何かしらのサーバサイドフレームワークと同時に運用することになります。
sbt-web-scalajsのサンプルを参考にするとそのあたりの設定が楽になります。
ビルド時間
Scala.jsのビルドには、それなりの時間がかかります。Scalaコードをコンパイルしたのち、JSコードを書き出す上、scalajs-bundlerなどを利用し、JS側の外部ライブラリの依存も持っていると、その分の処理コストもかかります。
また、サーバ/クライアントの双方でコードを共有している構成の場合、共有コードを変更すると、当然双方のコードを再ビルドする必要があります。
もちろん、これはJS側で別なAltJSを採用しても発生するコストなので、一概にScala.jsが故のデメリットとは言えません。
sbtのセッションを立ち上げたまま、ファイルの変更を監視しておけば、ファイルの保存時に勝手にビルドが走り、開発のテンポがよくなります。
> ~fastOptJS
のような要領です。
サーバ側も同時に稼働させている場合は、sbt-revolverを活用することで、ファイルの変更検知からビルド、再起動までやってくれます。
> ~reStart
生成されるファイルのサイズ
単なるHelloWorld
がfastOptJS
(ビルド速度優先の低圧縮)で88KB、fullOptJS
(最適化がかかるがビルド時間が長い)でも11KBでした。
これはまだ依存関係が小さいのでマシなのですが、ここに外部ライブラリや標準ライブラリへの依存が加わってくると、サイズは極端に大きくなっていきます。
ちなみに、手元のプロジェクトはfastOptJS
で4.4MB、fullOptJS
にしても1.6MBありました。
なるべくサイズを小さくするには、依存関係が大きくならないようにライブラリを厳選するなどの工夫は必要かと思いますが、そもそも最低限の動作のために(全体ではないにしろ)Scalaの標準ライブラリが必要になるため、カリカリに削るのは難しいと思います。
JSライブラリを使うためにはFacadeが必要
Scala.jsからJSのライブラリを使うには、Facadeが必要になります。
詳細は公式のドキュメントを見ていただければと思いますが、ざっくりと言いますと、JS側で定義されたオブジェクトに対して、そのインターフェースをScala側からアクセス出来るように型情報を付加してマッピングしてあげる必要があります。
有名なJSのライブラリであれば、すでにFacadeがOSSとして公開されている場合もあります。
また、TypeScriptの定義から自動生成することも可能です。
単なるFacadeではなく、よりScala的に扱いやすいよう、アレンジされたライブラリもあります。例えば、scalajs-reactはFunctionalなアプローチがScalaとReactに非常にマッチしていると思います。
Scala.jsのメリット
すでにあるScalaの資産を活かせる
Scala.jsでは、Scalaで書かれたコードは(JS環境依存で動かないものを除き)全て、JSコードに変換できます。つまり、すでにそこにあるScalaコードは、そのままScala.jsでも活かせる可能性があります。
また、資産はコードのみではありません。チームのメンバーが身につけた知識も組織の大事な資産だと思います。
未知のプログラミング言語を学び、正しいお作法で書けるようになるには、相当な学習コストが必要です。
ロジックの二重管理を避けられる
同じ判断や加工の処理を、サーバとクライアントで二重に書き、それをズレが発生しないようの気をつけながら二重に保守する...。そんな経験はないでしょうか?
例えば分かりやすい例で言えばバリデーション。満たすべきルールは同じはずですが、クライアント側でもチェックしたいとすると二重になってしまいます。
コードが同じであれば、そもそもこのような二重管理の問題を気にする必要が無くなります。
言語間の差異を気にしない
例えば、シリアライズ処理を考えるとき、言語を跨ぐと当然その間には仕様の差異があり、データ型にも違いがあります。言語間で同じデータ構造を共有しようとしても、うまく合致しない、あるいはちょっとした変更に対しても修正のコストが高くつく、ということが起こります。
Scala.jsを利用した場合、データ型は同一になりますし、JVM/JSの双方で使えるシリアライズのライブラリも多数あります。
Scala.jsが適さないケース
単なるAltJSとして単品で使うのは、ほとんど旨味がない上に、前述したようなデメリットのみがのしかかるので、悪手だと思います。
また、複数ページが存在するようなWebアプリケーションで、それぞれに異るJSファイルを設置するような運用には、まったく向きません。個々のJSファイルが大きくなってしまうので、ダウンロードに時間がかかる上、基本的に1プロジェクト=1JSファイルの構成となるので、ぺーじごとにプロジェクトが必要になってしまいます。
その意味では、JSのダウンロード時間に対してカリカリのチューニングが必要な案件では、まず使えないと思います。
エンドユーザ向けのがっつりデザインされたフロント、特に最新のJS/CSSをふんだんに使うような構成では、Facadeが存在せず、それらに追随することが難しいと思います。
Facadeを自作するくらいの覚悟を持って望むことも出来なくはないですが、本来集中したいのはリッチなフロントエンドの追求だと思いますので、なかなかペイしないのではないでしょうか。
Scala.jsが適したケース
Scalaの資産、リソースの活用
Scalaの資産、成果物や人的リソースなどをフル活用したい局面では非常に強いと思います。モチベーションとして、サーバ/クライアントの双方で同じロジックを共有したい、メンバーが書き慣れたScalaでいつものライブラリを使って高効率な開発をしたい、という場合などです。ブラウザ上での実行に限らず、Node.jsを経由すれば、コマンドラインツールとしても使えますし、AWS LambdaなどのFaaS向けとしても活用できますので、活用範囲が広がれば広がるほど、付加価値を高められるのではないでしょうか。
SPA/PWA
Webアプリケーション用途で使うのであれば、SPAが適していると思います。ファイルサイズの大きさの問題は、初回に一度ダウンロードしてしまえばあとはそのまま使えるので、そこまで気にならなくなります。また、ゲームやチャットなどのように、滞在時間の長いアプリだと、相対的に稼働時間に対する初回のダウンロード時間も小さくなります。PWAのようなダウンロードしてキャッシュしてしまうアプローチにもマッチするでしょう。
また、SPA特有の入り組んだ状態管理も、Scalaのスケーラブルで型安全なコードであればより書きやすいのではないでしょうか(個人の感想ですw)
フロントエンド専任者がいない
Scalaを書ける(書きたい)メンバーが複数いる上で、フロントを専門で書く人がいない、という状況ではおすすめです。特に、内部向けの管理画面のようなものを作るケースではサーバ側(Scala前提)との連携が強く活かせるので非常に有効だと思います。
バリバリのフロントが必要というわけではないけれど、ちょっとしたツールは必要で、書き捨てではなく、ちょこちょこと更新もある、という状況はまさにうってつけで、保守をする以上、普段は書かない言語やツールを採用するのは厳しいですし、属人化の恐れもあります。
まとめ
社内にScala好きがいて、新たにフロントエンドのタスクがあるのなら、採用を検討してみてはいかがでしょうか。
フロントエンドを考えるとき、バックエンドとは完全に切り離して考えてしまいがちですが、コードや思想、人的リソースを双方で共有できると、今までとは違った見方も出来るかもしれません。