Kengo's blog

Technical articles about original projects, JVM, Static Analysis and TypeScript.

プログラミング言語が抱える課題を解決するには言語を乗り換えるのが一番いい、かもしれない

この記事は集まれKotlin好き!Kotlin愛好会 vol.47の懇親会でちょっと触れた内容を、膨らましてブログ用にまとめ直したものです。

注意点として、Java用静的解析OSSの開発保守を長年やってきたJavaプログラマがKotlinに乗り換えて1年経ったころに書いている、という強力なコンテキストがあります。また「何十年前の話をしてんの?」という部分が多く存在しますが見逃してください 🙇‍♂️

プログラミング言語の成長はすべてを解決する

どんなプログラミング言語にも固有の問題は必ずあります。私が一番長く書いてきたプログラミング言語であるJavaでも、いくつかの課題が指摘されてきました:

  • 不要な同期を取りすぎている(StringBuffer, Hashtable, Vectorなど)
  • シリアライズ・デシリアライズが遅い(Serializableインタフェース)
  • コレクションにどのようなインスタンスが入っているのかわからない
  • null安全ではない
  • 記述が長くなりやすい

こうした問題に対して、Javaとそのコミュニティはとても良く対応してきたと思います:

  • StringBuilder, HashMap, ArrayListなどの同期を取らないクラスが標準で提供された
  • シリアライズ・デシリアライズ用ライブラリが充実した
  • ジェネリクスが実装されてコレクションに入っているインスタンスがわかりやすくなった
  • Optional が標準で提供されるようになった
  • より抽象的に扱えるように、try-with-resourcesやStream APIなどを順次導入してきた

これに限らず様々な改良も進んでおり、JVM(Java仮想マシン)の改善も相まってこれからも強力な言語で有り続けるだろうと思っています。また言語とは別の外付けの改良も進んでいて、インクリメンタルビルドの実現、アノテーションによるコンパイラやツールへの情報提供、ツールやIDEによる静的解析なども盛んです。

実行環境に対するコントロールが効かないアプリやオンプレサーバの開発ではわかりませんが、一般にこうした言語や周辺環境の成長の恩恵を受けることは近年とても容易になってきています。こうしたアップデートを即座に取り込む、あるいは自分で作りに行くことは、多くの生産性の課題を解決するうえでとても重要です。

それでも成長による課題解決には限界がある、特に初学者にとっては

それではJava言語にはもうコーディングの現場で考慮すべき問題はないのでしょうか。もちろんあります。むしろ解決済みのはずの問題にすら気を配らなければならないケースがほとんどです。

StringBuffer, Hashtable, VectorなどのクラスはまだJava言語に存在します。個人的につらいなと思っているのは Stack クラスです。「スタック構造がほしいときは Stack じゃなくて Deque を使いましょう!」なんて一見意味の分からない注意事項が現役なので。。。 Properties が現役なのもわかりにくい。

シリアライズやデシリアライズは言語機能ではなくライブラリを使いましょう、というのも初学者にはつまづきやすいポイントだと思います。しかも聞く人によってkryoがいいよ、いやGSONでしょ、あれJacksonじゃないの?なんてことにもなるわけで、やはり標準が定まっていないのはわかりにくいなと思います。

ジェネリクスや Optional は便利ですが、プロジェクトの依存先を含め多くのインタフェースで利用されて初めて価値が出ます。特に長命のプロジェクトでは、導入が難しいこともあるでしょう。また「 null が入りそうだからこのフィールドは Optional にしよう」みたいな誤用の機会も多く潜んでおり、正しく使うのはいつでも難しいものです。

強調したいのは、自分はJavaとJVMが提供する強力な互換性はとても好ましいと思っています。またJava Platform Module System (JPMS、旧称Project Jigsaw)を使って不要な部分を取り除いたJVMを自分で作れる未来は来るだろうとも期待しています。

が、それが実現したとしてもそれは今ではないですし、ここに挙げられていないいくつかの課題はきっと残るでしょう。そのうえ更に「ここは歴史的経緯で2つ選択肢があって、今はこっちを使います」のような要考慮点はきっと増えていると考えると、初学者にとってはちょっと辛そうだな、という印象です。

外付けの改良は中長期的には開発体験を損なう方向に作用する

ではアノテーションやIDE、ツールなどを駆使していけば良いのでは?という発想が次に来ます。SonarQubeのようなサービスを導入し、こうした課題をPull Requestレビューの段階で洗い出せば、初学者でも学びながらプログラムを書いていけるのではないでしょうか。

実際にこれは可能だと思います。私自身、JenkinsやTravis CI、GitHub ActionsなどでJSR305やPMDやFindBugs、SpotBugsにGoogle erorr-proneなどを組み合わせて使ってきました。IDE組み込みの解析機も良いものですし、GitHub code scanningやSonarLintも魅力的な選択肢です。

ではこれが永続的な解決かというと、ちょっとそうは思えないなというのが私の意見です。プロジェクトの成長に伴って実行時間がかかるようになりますし、どの警告を受け入れてどの警告を無視するかという判断も必要になりますし、棚上げした課題の棚卸しも必要です。インクリメンタル分析によって速度を改善しようとすると変更しなかった部分との咬み合わせがうまくいかなかったりしますし、俯瞰的に見て初めて見つかる課題をどうやって見つけて対応しようという課題もあります。

ので外付けツールはケアレスミスを洗い出したり中期的な改善施策を検討するうえでの材料にはなりますが、経験者によるPull Requestレビューに比べての教育効果はそこまででもないのでは…と思っています。そもそも言語の課題なのにさらに計算機資源と時間をつっこんで解決しないといけないということ自体が非効率という思いもあり、頼り切りたくは無い感じです。

言語を乗り換えると問題を一網打尽にできる、かもしれない

Kotlinに乗り換えれば、null安全やジェネリクスの課題は改善されます。またコードの長さも短縮されます(愛好会で事例をひとつ紹介しました)。ひとつのアクションで多くの課題が解決されるのは、新しい言語が人類が発見した知見を取り入れて設計されているからです。

外部ツールへの依存が減ることで、開発体験を改善することも期待できます。残念ながらKotlinのコンパイルはまだ遅いですが(手元で試した限りではK2もあまり解決にならない)、静的解析ツールの必須度が大きく減っていることから、相対的には改善されているかなと思っています。

もちろん言語を乗り換えても解決されない問題はあります。Kotlinでも古いコレクションは使えますし、外付けツールに頼っている問題発見もまだまだあります。ので私はここ5年とか10年とかはこうした解決を積み上げながら、また一網打尽にできる言語の登場を待つのでしょう。Flixみたいな実験的な言語をウォッチしているのも、新しい言語が得た知見を不格好でも今の言語に持ち込めないかと期待しているからです。

生成AIがすべてを解決するかもという期待

生成AIによるゲームチェンジを期待しているのが、こうした「人間が気をつけなければいけない」問題を劇的になくしてくれるのではということです。外付けツールとして今まで以上に優秀な解決を提供してくれるかもしれないですし、そもそも人間に書かせないことで問題を根っこから解決してくれるかもしれません。

ただ実行時間がどうしても遅いというところで、まだ自分が期待する「1分以内にフィードバックを返してくれる、開発体験を改善する存在」には遠いかなというイメージですが、これもあと数ヶ月したら変わってるかもしれないですよね。楽しみです。

あわせてよみたい