kmizuの日記

プログラミングや形式言語に関係のあることを書いたり書かなかったり。

Scalaの学習コストについての私見

※2022/01/25 17:10追記

 以下のツイートが別にScalaが主眼でない」こと自体は承知しています。一般論として、Scalaに限らず言える技術選定の話ですよね。ただ、妙な方向への読解をちょくちょく見かけたので、それに乗っかる形で現状のScalaについて率直な感想を書いておこうと思ったのでした。「この話とは別なんだけど」みたいな言葉をつけておけば良かったです。申し訳ありません。

 数日前、以下のツイートに端を発して色々な意見が交わされていました。

 ここで言われていることの一部は確かに正しいと感じます。Scalaについて言及する時はいつ頃かというのがポイントなのですが、たとえばScala言語そのものでなくても標準ビルドツールであるsbtが1.0未満の時代(はもう数年前です)だと色々扱いづらい部分も多かったです。また、Scala言語についても2.12以降はともかく2.11までは無節操に一部ライブラリを非推奨にしたり、かと思えばexperimentalとして導入されたマクロ機能がガンガン使われてたりして、難しいというよりややこしく見えた部分は多々あります。

 ただ、現状、つまり2022年時点でのScalaの学習コストはさほど高くないと考えています。もちろん、私の立ち位置上バイアスがかかっている可能性もありますが、それ以上にScala周辺の環境が「こなれてきている」のも確かです。

 というわけで、このブログでは、現在のおよび過去のScalaをめぐるあれこれについて思ったことをつらつらと書いてみます。雑にまとめているので不正確なところ多しですが、その辺りはツッコミいただければと思います。

sbt 1.x系列が安定している

 Scalaの学習コストについて盛んに言われた時代は標準ビルドツールであるsbtがまだ1.0になっていない時代でした。その頃のsbtのビルド定義ファイルは、よくわからない記号的演算子を使いまくったりしていて控えめに言っても読みやすいとは言えませんでした。しかし、この辺りの読みづらい演算子や良くない書き方については2017年リリースのsbt 1.0でかなり改善されました。まず、余計な記号演算子は非推奨になったり削除されたりしました。プロジェクトを定義するための方法も推奨の方法が用意されていて、以前ほどビルド定義がわけわからんという状況にはなっておらず、他言語のビルドツールに比べてひどいということはないです。sbtプロジェクト自体でsbtのバージョンを指定できる設計(sbtプロジェクト自体にsbtのバージョンをロックする機能がある)などは、gradleやmaven、npmなどより楽なことが多いとすら感じます。

 たとえば、単純なsbtプロジェクトを作るのなら、

name := "example"
organization := "com.github.kmizu"
version := "0.01-SNAPSHOT"
scalaVersion := "2.13.6"

 くらいの記述で済んでしまいます。もちろん複雑なプロジェクトはそう簡単にいきませんが、単純なプロジェクトはむしろsbtの方が圧倒的に楽と言ってもいいくらいです。なんといっても、キーと値の結び付けだけで最低限の設定ができるわけですから。しかも、キーの名前が間違っていたらちゃんとビルド実行前に教えてくれますから、実行時に初めてnameがnamだとわかるよりだいぶ楽です。

ここ数年以上、Scalaは仕様変更をほとんどしていない

 次のメジャーバージョン……というか、結構Odersky先生の趣味入ったScala 3についてはおいておくとして、現在安定して使われているScala 2.12やScala 2.13については(マクロとかのユーザが妙な作りこみをできるところはおいといて)Scala 2.11時代と比べても言語仕様はあんまり変わっていないというかほとんど追加もありません。コレクションライブラリに手が入ったり、処理系のチューニングが進んだりといった細かな改善は色々ありますが、昔のScalaみたいにライブラリの互換性を軽視しまくり(2.10くらいまでは実際そうだった)な姿勢は見られません。ちなみに、Scalaはメジャーバージョンアップごとに言語仕様をころころ変えているという誤解が一部にみられるようですが、これはあんまり正しくありません。現在のScala(2.13系)の言語仕様は2.8時代(2012頃)とほとんど変わっていません(追加機能はあるけど、消えた機能はprocedure syntaxとかくらい?)。正確には、昔はメジャーバージョンアップごとに互換性の無い形でライブラリの仕様変更をしていた、でしょうか。

 そのライブラリの仕様変更にしても、2.12以降は一通り落ち着いた感があり、今ではむしろ比較的安定しているのではないかと思います。Scala 3は色々変わったので話が別ですが、現場ではまだまだ普及してないと思いますのでここでは言及しません。

 一方、Javaはバージョンが上がるごとにがんがん色々な機能を追加していて、どのバージョンでどの機能がstableなのかexperimentなのかわかりづらい状態になっていますし、追加された機能が既存の部分とうまく噛み合っていない部分もあるしで、最新のJavaを追っかけるのはむしろ学習コスト高かったりします(以前から追ってる人はともかく)。これは別に悪口とかではなくて、往々にして学習コストてのは「イメージ」に左右されるんですよね。

 Scalaの場合は初期に処理系が不安定だったことや、ライブラリ作者があんまり互換性を(昔)重視しなかったことや、記号メソッド使いまくりの中二病ぽい設計のライブラリがいっぱいあったのが学習コストをあげていた要因だろうっていうのがまず一点。他には、一方的に他言語をディスる(人のことはいえませんが)人の存在とか、必要以上に物事を厳格にとらえる人たち(私もその一員でした。はい)もあって、Scala難しいって印象につながったんではないかなあと。たぶん、同じくらい厳格な立場でいうとJavaもすっごい難しい言語に入るはずですし。

 この辺りは技術というよりアカデミア出身の人が多い故に厳格な人が多く、かつマーケティングを軽視する人が多かったことが原因といえるかもしれません。実際、Scala難しいならKotlinも難しいとなりそうなくらい両者は似ている……というより、KotlinはScalaの主要な機能をほぼ継承しているのですが、その辺りはJetBrainsのマーケティングが上手なのと、ライブラリの互換性を軽視しなかったこと、厳格な立場をとる人があまりいないこと、JetBrainsの政治がうまい辺りが主要因だろうなと感じています(これは悪口ではなくて、逆にLightbendの言語マーケティングが上手くなかったというのが正直な実感です)。

定番フレームワークやライブラリが一通り出揃っている

 たとえば、jsonを扱うならcirceがメジャーですし、Webアプリケーションなら他にも選択肢があるとはいえ、Play Frameworkデファクトスタンダードです。DBアクセスならScalike JDBCが国内でよく使われています。分厚いO/RマッパーではなくSQLを単純なオブジェクトと結びつけるタイプの薄いライブラリなので使いやすいです。

 APIサーバーだけを立てたいのならAkka HTTPなどもあります。今時はSPAだよねーという方はテンプレートエンジン前提だったPlay Frameworkよりこっちの方が使いやすいと感じるかもしれません。

 いずれにせよ、Webアプリケーションを作るという目的からすれば、結構色々なライブラリが揃っていて、しかも成熟しているものも多いというのが現状です。もちろん、Scalaのライブラリだけだと足りないことも多くて、その時はJavaライブラリを呼び出すのが定番ですが、この辺はKotlinとかも同じですし、Scalaが特に面倒くさいということもないです(両方とも大体シームレスにJava API呼べるので)。

ライブラリのバイナリ非互換性の問題は「それなりに」なんとかなる

 Scalaの欠点として従来言われていたものに、メジャーバージョンアップすると、ライブラリのバイナリ互換性がなくなるというのがあります。たとえば、Scala 2.12用にビルドしたライブラリはScala 2.13用にはそのままでは使えません。これを聞くとたぶんScalaについて詳しくない方は「ウゲ」と思われるかもしれませんが、sbt自体がクロスビルドという、同じソースから複数のメジャーバージョン向けのバイナリをビルドする機能があって、メジャーなライブラリは複数のメジャーバージョン向け(たとえば、2.12用と2.13用)のバイナリを大抵同時に用意してるので部外者が思う程大きな問題ではありません。

 この問題で苦労するのは、どっちかというとライブラリ開発者の方でして、クロスビルド出来るようにするために色々面倒な作業をしてくださっていることも多いです。逆に単にライブラリを使う側は、開発者が苦労してくれている分、そこまで負担を気にしなくて良い部分があります(しかし、ライブラリやフレームワーク開発者がしんどい思いするのはどうなんだとか、各ライブラリが新しいScalaのメジャーバージョンに対応するまで「待つ」が必要とかはもどかしい問題ですが)。ともあれ、苦労はあるけど、破滅的な影響があるわけでもありません。

やっぱり書籍は少ない

 Scalaがもっとも注目されていたのは2010年前後だったように思われます。この時期はScala 2.8くらいの頃でビルドツールも標準的なものはないし、処理系の品質もイマイチだし、みたいな部分があったので、この時期の悪印象が染みついている方もいるかもしれません。この辺の事情は本当にがらっと変わったのですが、そういうふつーのScala開発の事情を伝えてくれる日本語書籍が少ないために、必要以上に難しくとらえられてしまっている部分はあるなあと感じます。ちなみに、手前味噌ですが私たちが共著で執筆した『Scala 実践入門』はScalaをお手軽に学ぶのにうってつけだと信じています。読んでいただければ、意外にScala開発って「ふつー」ってことに気づいていただけるんじゃないかなと思います。

gihyo.jp

 とまあこんな感じで、現在のScala開発はまー特別「すごい」というわけではないにせよ、良くも悪くも普通です。Scalaなりの欠点はありますが、ぶっちゃけ他の言語とその辺りは大差ないです(他の言語も色々習得している人間の視点として)。

 あと、Scalaはこれ以上ないほどに現在のオブジェクト指向言語の延長線上にある言語なんですが、その指向性がどうも正しく理解されてないのも問題です。というのは、Scala学びたいけど、関数型だし……という声を凄くよく聞くからなんですが。そういう人にはScalaオブジェクト指向言語なんですよ、といってあげたいのが本音です。

 これは方便ではなくて、実際問題Scalaは仕様的にはオブジェクト指向言語としか言いようがないのです。作者であるOdersky先生はいっつもオブジェクト指向と関数型の統合が大事と言っているくらいオブジェクト指向スキーな人でして、Scalaの言語設計にもこれは顕著に表れています。たとえば、MLなどではint -> intという関数の型は言語にとってプリミティブですが、Scalaで同等の表現であるInt => IntFunction1[Int, Int]シンタックスシュガーでしかありません。Scalaは典型的なクラス指向オブジェクト指向言語と言っていいくらいオブジェクト指向的だと言えます。それにうまいことシンタックスシュガーかぶせたり、関数型的な概念をオブジェクト指向の立場から再解釈した機能をうまく取り込んでいるので、「他の関数型プログラミング言語と似ている」ように見えるだけです。

 implicit parameterとかimplicit conversionとかはぶっちゃけ説明が悪かった、って側面が多分にあって、この辺はScala 3でも反省があって用語とか含めてもっとわかりやすいものになっています(Scala 3には別の問題がありますがそれはさておき)。

 あと、私自身Scala教育をそれなりに担当して来ているんですが、普通に他のプログラミング言語を理解している相手なら理解してもらうのはそれほど難しくなかったというのが正直な実感です。もちろん、背景には私が関わっていたケースではScala教育に関するリソースが充実していたということはあるにせよ、逆に言えばそういう環境さえあれば教えるのは難しくないことの証拠でもあるかなと。

 以上、駄文でしたがScalaに普段触れていない方の参考になればと思います。