初めに、そして結論
本当は冒頭にこういう言い訳がましいことを書くのは好きではないのだが、例によってディスり成分が多めなので、筆者はそういうキャラということでそこは目を瞑って欲しい。実体験に基づいて書くので業務上あまり触ってこなかった言語については記載が少ないが、読者のみなさんが詳しいことや私の記述で事実と異なる部分については謝罪して訂正したいのでプルリク、コメント等熱望している。あと筆者はサーバーサイドエンジニアであってフロントの事が全然わからんのでその辺はさっぴいて読んでほしい。結論から言うとRustを使え。
古生代
アセンブラ以外の高水準プログラム言語の歴史は1950年代から1であるが、筆者の実体験に基づいて話をするので'80年代まですっ飛ばす。
N88-BASIC
N88といえばN88-BASIC(86)を思い浮かべる読者が多いと思うが、筆者はPC-8801mkIIユーザーだったのでN88-BASICである。当時は各社各シリーズごとに独自仕様だった。Z80A相当の8bit/4MHzのCPUの上で動くBASICインタープリタは大変遅かったが、パソコン2黎明期に各社から販売されているソフトウェアはBASICで書かれていた。筆者はBASICが書かれた国産初RPGのBLACK ONYXのプロテクトを外し3、任意のキャラだけセーブしてアイテム複製したり、初代信長の野望4の敵NPCを強くしたりOverflow errorがでないように5改良していた。自分を強くするチートは一瞬でゲームがつまらなくなるだけなのですぐやめた。
当時のBASICでは行番号という概念があり、実際の行数を指定すると、途中で処理を挟みたくなったときにそこから後ろの行をすべて修正する必要があった。このため行番号を10行単位で書くというのが主流だった。GOTOやGOSUBで行番号指定することもできたが、これは別にLABELを指定することもできたので、行番号で指定するのは色んな意味で悪手だった。変数のスコープは全部グローバルだし、全体的に考古学でしかないので学ぶ必要はない。
一部のおじさんたちが教育用にBASICを勧めたりしているが、ただの懐古主義である。当時は手に入った最新の環境がBASICでありそれがたまたまシンプルだっただけで、今筆者が子供にプログラミングを教えるならjavascriptで教えて自分の持ってるスマホで気軽に遊べるようにする。あんなシングルスレッドのゴミ言語、大人相手なら教えないが。
Lisp
(募集(プルリク(熱烈)))
中生代
C
Cは70年代からあるが、筆者が業務で触ったのは90年代なので90年代の話をする。C99はまだなかったのでコメントは/* */
しか受け付けない。Cはマシン語をハンドアセンブルすることなく、人間の思考でロジックを組んで機械語にコンパイルできるようにしたことに価値がある。できるだけ機械の気持ちに寄り添って最速コードをアセンブラ以外で書くなら唯一無二の選択肢だった、当時は。
C++
中生代におけるC++は、Cの構造体に関数をかけるようにしてクラスを表現し、OOPの礎を築いた。今でこそOOPの本質はメッセージパッシングだとかOOは一兆ドルの損失とか言われているが、当時はCしか書けない平々凡々な人達から「難しいことをやる意識高い言語」という扱いだった。またhello worldのバイナリサイズをCのそれと比較して「C++デカすぎwwww、なんでhello worldするのにそんな大きくする必要があるのwww」と煽られていた。後のJavaが当初disられていたのと同じような状況である。その後もC++はどんどん進化を続けて仕様が膨らみ実装が追いつかないようになってしまった。新しい仕様が決まっても現場で使えるようになるまでかなりのタイムラグがある。過去との互換性を重視しているので仕様が膨らみ過ぎた感がある。
Delphi
Win32アプリを作成するには最強だった開発環境。VisualBasicライクにポトペタでフォームが作成できる上に、標準ライブラリのVCLがMicrosoftのMFCに比べて格段に出来がよかった。アンダースヘルスバーグ率いる開発チームが経営陣ともめて離脱し、Microsoftに合流した。彼についてはC#で再度言及する。
Haskell
純粋関数型言語はいくつか生き残っているが、その中でも関数型言語のメリットを世間に広く知らしめたのはこの言語ではなかろうか。それまで関数型言語といえばLispであり「()の多い変わった変態言語」「アカデミックかEmacsの設定用のEmacLisp以外で使ってるとこあるの?」みたいな扱いだった。純粋関数型言語には変数がない(すべて定数であり再代入不可)、副作用を外に出す、などの特徴によりデバッグがやりやすくなるというメリットを享受しやすい。マルチパラダイム言語だとどうしても自分の出身の言語の組み方をしてしまいがちだが、一度Haskellをやってみるといろいろ矯正されるので面白いかもしれない。
新世代
Java
Javaの登場は'95なのでC++と世代を分けるかどうかは悩んだのだが、C++を踏まえた上で改良した言語なのでここに置いてみた。C++での菱形継承の問題を避けるため継承元クラスは一つだけとして他はinterfaceとした。OOPに不慣れな人がただの共通処理を継承関係にしてしまい6、あとから機能拡張しようとして継承関係がにっちもさっちもいかなくなってしまうようなアホなケースが減った。ヘッダも必要なくなったし、循環参照に気を使う必要もなくなった。なによりも大きいのはGCを導入してメモリ管理を自動化したことと、VMを導入して"Write Once, Run Anywhere"を実現したことで、後に続く言語に大きな影響を与え、JVM上で動作する言語も多数生み出すことになった。
ゆっくりだが進化は続けており、JDK1.5でgenerics、JDK8でlambda式やstream APIが導入された。streamは後付け感がひどすぎて使いづらいがまあ無いよりはマシだ。lombokやvavr(旧称JΛVASLΛNG)を使うことでJavaの苦しみを大分軽減できるので、未だにJavaを使うことがあればこれらは必須である。
Javascript
当初Livescriptという名前だったが、Javaが急速に流行ったときにNetscape社とSunが業務提携して名前をJavascriptにしたせいでものすごい混乱を生んだ。未だにJavaとJavascriptは別物ですとか言う説明をしないといけないの、先見の明がなさすぎるとしか言いようがない。命名ルールにlowerCamelCaseを採用してる点だけはC/C++と区別できる共通点ではあるが、そもそも文法がC系(if(a==b){}
等)の言語が多数ある今となってはJavaとの共通点を見出すほうが難しい。
C#.NET
MicrosoftはJavaのライセンス条項を無視し勝手に拡張して、IEのブラウザ上でしか動かないActiveX連携できる謎の囲い込み実装を作ったり、Javaの名前を使うなと言われてJ++などという言語を作ったりしていた。このJ++プロジェクトに前述のDelphiの開発チームという当時世界最強チームの一つを入れた。Sunに訴訟で負けたあとはJavaをVMの思想からパクって.NET VMを作成し、Microsoftの開発言語を.NET上で動かせるようにした。C#の文法がC/JavaっぽいのにクセがPascalっぽいのはDelphiを作ったヘルスバーグが.NETのチーフアーキテクトだからである。その後組織が巨大化して進化が硬直化したJavaを尻目に、C#はどんどん成長を遂げる。
MonoなどのWindows以外で動くVMもできたが品質は微妙で、UI周りまで含めて"Write Once, Run Anywhere"できるわけでもなく、Windows Serverの管理の面倒くささとあいまってサーバーサイドを.NET系で作ってる会社は地雷というイメージがある。WindowsネイティブやWPFアプリをつくるなら良い選択肢だと思っていたが、そういう時代ももはや過ぎつつあり、ほとんどの業務システムはWebになった。ゲームならUnityやUEなどのゲームエンジンを利用することになるが、UnityでC#は現在でも良い選択肢だと思う。
メソッドを大文字で始める習慣だけは慣れないが、C#の言語仕様そのものは評価できる。
現代
Scala
Javaに関数型のメリットを足したマルチパラダイム言語。Javaのライブラリがシームレスに利用でき7、Javaで貯めたJVMの知見が利用できる。ビルドシステムにsbtという地雷が用意されている8。関数型が広がり始めた頃にいち早く業務に耐えうる品質になったため、関数型に理解を示す現場で早期から使われ始めた。そのせいか関数型もしくはScala原理主義的な人が多く手続き型的なコードを書くと罵倒される。コミュニティが怖い。もちろん全員ではなくいい人もいるが、一部の人がKotlinやGoをディスっていて、かつその人が日本のScalaの第一人者的存在のため誰も咎めることができず感じ悪いまま放置されている。
Typescript
2010年初頭に雨後の筍のように乱立したAltJSのうち、本命として生き残った言語。Javascript自体がECMAscriptとしてある程度AltJSで提案された機能を実装したので、AltJSブーム自体は去ったが、業務用言語に型は必要だということでこれだけは残った。あとはHaskellに影響を受けたElmが残っている。
言語としてはJavascriptとの互換性を残すためなのか、2012年に設計されたとは思えないほど古臭い。JavascriptがTypescriptとしてもvalidであるというルールに準拠しても、TypeSafeEnumの導入やif/forが式として値を返すことはできたはずだ。あとフロントはパッケージ管理周りがアホなのでアホ。趣味でフロントをやるならまずElmを調査したい。
後輩が言ってたの面白かったので。
「Bowerって何?」
「パッケージマネージャ」
「どうやっていれるの?」
「npm使う」
「npmって何?」
「パッケージマネージャ」
「どうやっていれるの?」
「homebrew使う」
「homebrewって何?」
「パッケージマネージャ」
Kotlin
IntelliJ IDEAで有名なJetBrainsが作ったJVM言語。JavaとScalaの中間くらいの機能なので「なぜScalaより後出しでこんなものを作ったの?」と散々disられていたが、JetBrainsの回答は「Scalaで満足している人はKotlinを使う必要がない」だった。Scalaに非常に影響を受けていて、普段の記述はScala並に簡潔にかけるにも関わらず、mavenやgradleで普通にビルドでき、Scalaのimplicit黒魔術やマクロに悩まされて悩むことがない。マクロが完全に無いのはフレームワークなどを作るときに困るが、kaptを使えばプリプロセッサみたいな事はできる。あとパターンマッチがないのがつらい。when式(switch文の進化版)で耐えるしか無いが、Kotlin2.0ではパターンマッチ入ってほしい。
Kotlinは言語仕様としては関数型を目指していないが、Javaの項で挙げたvavrを利用することでEitherなども利用できる。nullable(String?等)は気軽にJavaのライブラリからnullを持ち込んでくるので、正直あまり有用でない気がしていて素直にOptionを使ったほうが良い。JavaのOptionalはSerializableじゃないのとか謎仕様でいけてないし、Option/Either/Listなど色々vavrで統一すると開発効率があがる。vavr-kotlinやvavr-jacksonの存在によりかなり便利に利用できる。特に後者はKotlinのdata class <-> JSONのコンバートが楽にできるので良い。ただjacksonはねえ、アノテーションがねえ。まあjackson一つ覚えるだけでjson/xml/yaml/typesafe-configなど様々なフォーマットが統一的に扱えるのが素晴らしい。
本格的に関数型したい向けにはΛRROWがある。Scalaの有名な関数型ライブラリcatsのコミッタなどがメインコミッタとなり、kaptを使って高階型なども実現している。その分利用するハードルはvavrより高い。
Ruby
def 募集
プルリク "熱烈"
end
Python
熱烈
歓迎
プルリク
Elixir
古の動的型付け言語Erlangを元にした言語。WebFrameworkのPhoenixとの組み合わせを業務で使っていたが、RoRに影響を受けたのか過度なCoCと時代遅れのクソ密結合ではっきり言ってセンスないと思った。大体メインソース置き場がlib
ておかしくない?Unix畑で育ったのでdeps
こそがlibs
になるべきだと思う。さらに奇妙なのはPhoenix2.0以降のディレクトリ構成だ。
<project名>/
|- lib/
| |- <project名>
| | |- (Modelクラス群)
| |- <project名>_web
| | |- (Controller/Viewクラス群)
なんでが何度もでてくるの?作者はこの方式を強制しないとは言っていたけど、project名_web
配下にviewを置かないとviewのpath指定を省略できない(特定のパスだけ省略できる事自体が時代遅れCoCっぽい挙動)、標準と異なるとわかりづらいので結局これに従うしかなく、作者のセンスがもろに出てる。そもそもRoRを拠り所にし(ry
@specによる型指定は気休め程度だし、関数宣言文と別の行に型制約を書く羽目になるし、存在しないモジュールのimportがコンパイルするまでエラーにならない(これは言語のせいというよりはVSCodeやIDEAのpluginの問題)し、二度と業務で動的型付け言語を使わないぞ、という決意を新たにした。
Go
Google発のシンプルで"誰が書いても同じコードになる"を目指した言語。筆者も「将来はserverlessでGoでmicroservicesだ!」と思って調査したことがあるが、あまりの機能の少なさに勉強すればするほど「コレじゃない」感が溢れて止まらないよおぉぉ。Googleのように頭の良い人達がいっぱいいる会社でこんなに機能を制限した言語を使うことにメリットあるんだろうか?SIerからゴミみたいなコーダー集めて人海戦術でなんとかする場合には有力かもしれない。
次期Goでgenericsとerror handlingに改善が入ると言っているが、まだproposalでインターフェースを議論している最中で、β実装とかのレベルではない。いつになったら使えるのか。
https://github.com/golang/go/issues/36338
そしてGoのgenerics対応を調べていて強く感じたのは、genericsだけ対応してもinterface{}型
からのキャストをしなくて良くなるだけで、lambda式や型の継承がなければ結局便利にならないんじゃないかってこと。Maybeモナド(Java8でいうところのOptional)やEitherを使ってコードを保守しやすい状態に保ちたいのに、genericsが入った程度じゃ焼け石に水だ。
さらにCの置き換えを目指してる(?)こともあり古生代のCのカルチャーが蔓っているいる。筆者はScalaでOOPして「1メソッド10行、1クラス100行超えたらやばいコードスメルだぞ」という感じでやっておったので、if err != nil{}
を書きまくり、手続き型で1メソッド50行なんて当たり前、なんなら100行超えてもいいぞみたいな空気にはゾワゾワが止まらない。30年前にいっぱい見たよこういうコード。
Rust
Mozillaが開発したマルチパラダイム言語。GoがモダンなCだとすればRustはモダンなC++である。CとC++ならCの方がより機械に近い分有利な点もあったが、Go vs Rustだと速度やランタイムの小ささでもRustに軍配が上がる。なのにOO+FPができ、導入するのに必要な知識も少ない9。先程あげたMaybeとEitherについてはRustは標準でOptionとResultを用意していて、関数型やモナドの知識なしにみんな使いこなしていてすごく良い。
デメリットはユーザーが少なすぎて業務で使用するにはかなり勇気がいること。StackOverflowのMost Loved Languagesでここ数年1位を取り続けているにも関わらず、実応用の話はとても少ない。このアンケートに答えるような人は本来の良い意味で意識が高い10early adoptersであるため現場に浸透するまでタイムラグがあるだけで、近い将来流行ってくれることを祈っている。
そしてベストプラクティスがまだ成熟していないため、よく利用されるパターンが数年でひっくり返る可能性が高い11。つい最近もメジャーなWebFrameworkだるactix-webで一悶着あった12。
自作OS界隈ではC++の代替としてRustを使うのがかっこいいみたいな空気になっているようだが、まだ小さいコミュニティの話だし10年後どれだけ使われているかを考えるとなかなか怖いものはある。
まとめ
脳みそを有効活用したいならRustを使え。
-
1954年にFORTRANが考案された。高校の「計算機数学」の時間に習ったが授業中はテトリスばっかりしていてなにも覚えていない。https://ja.wikipedia.org/wiki/FORTRAN ↩
-
80年代初頭はマイコン(my computerの略)の方が主流だった。マイコンをもっていない人は自分たちのことをナイコン族(無い computer)と自嘲気味に呼んでいた。 ↩
-
最初期は様々なソフトがプロテクトなしで販売されていてStopボタン(Ctrl+Cにあたる)で止めて
LIST
と打てばソースコードが表示できた、次第にOS標準のプロテクトがかかるようになったが、これはOUT
コマンドで解除できた。後にコピープロテクトなども含め各社プロテクトをかけるようになった。BLACK ONYXではソースを改造すると起動時のチェックに引っかかってリセットがかかるようになった。 ↩ -
17ヶ国版。プロテクトはかかっていなかった。メインプログラムはBASICだったがHEX戦闘画面ではマシン語が使われていた。もしかしたら画像データだけかもしれない(N88-BASICでグラフィックに線一本でも引こうものなら秒単位の時間を要する)。 ↩
-
N88-BASICではIntが16bit符号付きになっており、上限が32,767である。民忠誠、民疲弊を20,000以上にすると兵糧が大量に手に入るし、兵士忠誠を20,000以上になると少数でもほぼ無敵になるのだが、やりすぎて32,767を超えるとエラーでゲームが中断してしまい、
CONTINUE
で復帰することもできなくなる。筆者は事前に上限を計算して画面上に表示することでOverflowで中断が起きないように改造していた。 ↩ -
継承より委譲、1995年に出版されたGoFのデザインパターンにはすでに書いてあったが、2000年以降もこの基本がわかっていないアホが散見された。なお改訂版以降はUML&Javaで書いてあるが初版はOMT&C++だった。https://amzn.to/31h0SFP ↩
-
異なる言語のライブラリを呼び出すFFIは様々な言語にあるが、Scala=Java=Kotlinの相互呼び出しは自言語の関数を呼び出すように普通に呼び出せる。 ↩
-
mavenやgradleでもビルドできるが、Scala界で一般的でないため情報が少なく、各種ツールがsbtを使っているためsbtを覚えざるを得ず、結局覚えるのならsbtを使ったほうが良い。 ↩
-
例えばKotlinならgradle,maven,JVMについての知識を同時に覚えなければいけない。他人が作ったプロジェクトにヘルプで入るだけなら言語だけに集中できるが。 ↩
-
筆者のように陰キャのコミュニティでは「意識が高い」とは中身空っぽのワナビーを指す悪口である。フルスタックエンジニア()とか。 ↩
-
筆者は実際に業務で使ったわけではなく、ネットでみかけた他人の感想である。 ↩
-
メンテナがactix-webのトップページのREDME.mdを「actixの死後」という題名で長いポエムを書いた。不躾なユーザーのわがままには付き合いきれないといった内容だった。すぐに主要コミッタの一人が代わりのメンテナになることで事なきを得た。 ↩