この記事の趣旨
レガシーシステムについて考えていると意外なところから依存関係の逆転が出てきたのでそれについて話す。
レガシーシステムとは
レガシーシステムとはソフトウェア業界では一般に嫌われる。しかし、レガシー=遺産であって決して負の遺産だけではないはずである。
インフラ業界ではレガシーシステム=枯れたシステムでバグが出されきった状態としてレガシーシステムを重宝したりする。枯れたとかネガティブな単語使うし、重宝されるのはただ単に内部実装が触れないほどスパゲッティでそのまま使うしかないのが大半なのでやっぱり負の遺産だったりする。
正常なシステムであれば追加機能は既存のシステムとバッティングしないように作れる...(はずだ)。
仕様変更
メジャー言語でもたまに起こる。古くはCとC++の分離、C++とC言語は全く別の言語です。
Python2からPython3への切り替え、Linuxの管理系プログラムもPython3に全面切り替わりでPython2はインストールされないようになると嬉しい。
そして、さらし者のように残り続けるJavaScriptのfor in。
まあ、仕様変更はたまに起こります。
Unix哲学
Unixにも仕様変更が起こりオープンソースではなくなったのでそのクローンのLinuxが誕生しました。そしてこのLinuxがとてもUnix哲学に乗っとてるとは言いがたいんじゃないかと思いました。何が?
パッケージ管理システム
パッケージ管理システムは便利ですね、ないと生きていません。しかし、これがUnix哲学に真っ向から背いています。
ディストリによってコマンドが違うとか可搬性はどうした!
それでも困りません。いろんな解説ページがあるからです。しかし、手動で何かをインストールすることになるとmakeを叩かされ、ライブラリがないことに困ったり、./configureで使用要件を満たしてないと怒られcmakeでバージョンが古すぎると怒られます。
可搬性はどこ行った。
斜め上からの回答
C/C++からWebAssemblyにコンパイルするによると、全部自前でコンパイルしろ。
いや、普通にパッケージ管理システムにemccあるし...、そんなの知らんとりあえずコンパイルすれば出来るんだからおんなじことをやれ!というスタイルです。
コンパイルに2時間弱かかったけどしかも丁寧にCPU全部使いきって!ほぼ99%CPUを使うのでその間20年前のPCに戻った気分...、その後パッケージが見つかった絶望感
シェルスクリプト
そもそもシェルコマンドがWindowsの開発環境が糞なのは置いといて、約9割のシェアを備えるWindowsに対応してない時点で...。
スクリプト自体もオプションが複雑すぎなコマンドもあったり微妙に仕様が違うコマンドがインストールされていたり、そもそもリストも連想配列もない言語でごちゃごちゃやるのは罠の製造機にしかなりえません。
言語に付随するパッケージ管理システム
ということで最近のスクリプト言語にはパッケージ管理システムが付随するのが普通です。そしてそれがついたpythonでいろいろ管理してとかやろうとすると2系と3系で悩まされます。
NodeとJavaScript
本題です。NodeはJavaScriptですがブラウザでNode系を走らすことはほぼできません。逆にNodeでブラウザのAPI利用することはほぼできません(Electronとか使えば可能かもしれませんがそういう仕掛けなしにという意味で)。
しかし、最近ではブラウザで実行するためのJavaScriptはNodeで作られたツールを使って作ります(どうやらimport/exportに対応したらしいのでes6のJavaScriptならトランスパイルなしで実行できるかもしれません)。
何故、同じJavaScriptでも実行できないのか?それは実行実体が違うからです。JavaScriptはクライアントサイドの言語なのでブラウザがスクリプトを読み込みコンパイルして実行します。その際、ローカルのファイルシステムにアクセスできたら恐怖ですよね、勝手に.ssh/configを盗み見してとか...。一応、ユーザーがファイルを指定してとかはできますが安全上、黙ってブラウザのJavaScriptからファイルシステムにアクセスはできないはずです。
それが何故、依存関係の逆転か?
Nodeで書かれたツールがあなたの書くJavaScriptファイルがどんなものか知りません。逆にあなたもよほど奇特でコードを読んだことがないプログラムは動かさない!とかいう心情がない限りはbabelやwebpackの内部コードを読んだことがないと思います。しかし、それでも両方きちんと動きます(あなたがJavaScriptの規約に則る限り)。
つまり両方共、JavaScriptの規格という抽象に対してプログラミングしたのでお互いを知らなくてもうまく動くようになりました。
ここで逆転が起きたのでじゃあ、Nodeは何ができるの?というとファイルシステムfsとpathモジュールを持っているのでだいたいシェルと互換の動きはできます。最終的にコマンドを叩く際はchild_processで実行もできます。
じゃあシェルスクリプトいらなくない?
自分は多分、Yesですと答えます。
更に上位レイヤーについて考えてみる。
NodeはC++で書かれている。各ブラウザのエンジンも多分、C++だろう、FirefoxはRustで実装してるかもしれないがしかしNodeやブラウザのJavaScriptエンジンが何で書かれてようが
NodeがC++で書かれていることは知っていてもそのソースを見に行く人は稀だろう。つまり、粗結合になっており依存度は逆転しても問題ない。C++がレイヤーアーキテクチャに当てはめるならインフラストラクチャだろうか?。
C++はどうかというといかにもハードウェアに依存しまくりなイメージがあるがそれを吹き飛ばしたのがうえで紹介したC/C++をブラウザで実行しようというWebAssemblyの試みである。これにはLLVMという仮想化が絡んでいる。そう仮想化することによりOSですら切り離し可能になった。
もはや開発環境を仮想化してそこに開発言語のパッケージマネージャでライブラリをインストールしては当たり前である。
悪貨は良貨を駆逐する
しかし、この気付きを与えてくれたJavaScriptの開発環境はカオスだ。やっとブラウザが正式にes6を網羅しはじめたがもはや素のJavaScriptですらトランスパイルが必須でそのトランスパイラはWebpackというローダーで設定され、更にAngularやReactやVueなどのJavaScriptで書かれたフレームワークを使い挙句の果てにAltJSという別言語を使う。
何故こうなったか?
JavaScriptはクライアントの言語であったがAjaxでサーバー側にデータを送受信することが可能になった。そこからサーバー側でもJavaScriptで書きたいという要求が生じNodeが生まれた。
それはいい!
しかし、サーバー側とクライアント側で同じ言語が使えるのでそれを混ぜてしまおうとJavaScriptフレームワークが出来上がった。それはサーバー/クライアントで分けられていたレイヤーを混ぜかえしただけではなかったのではないか?
HTMLはプログラミング言語ではないからUIとして成立させられない、だからHTML+JSでUIを作る。しかしHTMLはあくまでもマークアップ言語でデザインはできないだからcssが必要になる。
ならcssタグもJavaScriptでいじろう。HTMLとcssとJavaScriptが合体したところに更にサーバーサイドが悪魔合体した、と私は思う。
依存性の逆転したモジュールは良いモジュールだ
レガシーシステムについて考えていると意外とLinuxとシェルスクリプトがUnix哲学に反してるんじゃないかと思っていろいろ考えていると、JavaScriptがサーバーとクライアントをまたぐので依存関係が逆転していた。そして、依存関係が逆転したJavaScriptを使えばOS非依存の処理が書けると思った。
もちろんそれはJavaScriptのfsやpathモジュールがOSの依存性を包んで隠しているからであるがパッケージ管理システムを持っている言語なら大概、同様のモジュールは持っているはずなので、今わざわざ罠の多いシェルスクリプトですべて処理する必要はないという結論に至った。
最後にnodeがインストールされていないマシンは結構多いそこでインストールしてみよう。
$ wget https://nodejs.org/dist/v6.11.4/node-v6.11.4-linux-x64.tar.xz
$ xz -dv node-v6.11.4-linux-x64.tar.xz
$ tar xfv node-v6.11.4-linux-x64.tar
なんとダウンロードして解凍したらnode-v6.11.4-linux-x64/bin/nodeに実行ファイルがあった。
多分サーバー側でUserAgentでOSを見極めて処理を分けているんだろうがメガテンだ!
まあ、どの程度使えるかはわからないのでもしかしたらリコンパイルするかもしれないが
ちなみにtar.xzというファイル形式は古いシェルだと解凍できないらしい、この事実ひとつ取ってもいかにシェルスクリプトが棄てられるべき存在かわかる。
これらに使われたコマンドはすべてPOSIX非標準です、まあとっくの昔に終わったUnixコマンドなんて使い物になりませんね...。
本当は斜め上からの回答が以外と的を射てる的な結論で終わるつもりだったが。更に上からの回答が出てきた気分である。
Nodeの3種の神器
path、fsとchild_processはあったのでとりあえず使えそう。
ではどこからカオスが生まれるのか
それは逆転が発生したポイントではないだろうか?JavaScriptで言うと無理矢理サーバー側の処理とクライアント側の処理を押し込めてすべてひとつのフレームワークで片付けようとしたからだと思う。
そこにブラウザベンダーの凶悪なベンダープリフィックスがあったり、各ブラウザで対応してないAPIがあったり、そこにあるものを仕様変更して結局2年実装できなかったimportとか(素直にrequireでよかったと思うし今度は逆にNodeはimportは実装しないだろう、そういうライブラリがあるらしいし)。
Pythonの2系、3系問題もそろそろ2系は駆逐されそうだし。
Unixが閉じたパンドラの匣は、Linuxが開け、今、最後に残されたものを探しに行こうとしている。
それが希望か絶望かはたまたさらなる混乱か?
後コンパイル型言語さんたちから怒られそうなので
C++もファイルシステムもっててパス解決はOS非依存になりました、とか。
FirefoxはRustで書かれてます(予定)とか。
Goはチュートリアルがブラウザでできますとか言っときます。
彼らの頑張りが振りまくのは新たな混沌か希望か。まあ、絶望する前に覚えることがいっぱいです。
最後に
wiki プログラミング言語一覧---いちいち取り上げてたらキリがない。
wiki hello world一覧---ネタ言語が半分以上占める