見出し画像

Retro-gaming and so on

UbuntuのSpiderMonkey

メモ。

JavaScriptはいい言語だ。
Cの皮を被ったLisp」と、東京上野クリニックが喜びそうなあだ名がある言語だ。

何度か書いた事があるが、オリジナルのJavaScriptの作者、ブレンダン・アイクはプログラミング言語Schemeの大ファンで、噂では、かつてNetScapeで雇われた際に「Schemeを使わせてやる」と言う口説き文句を言われたそう。
だから元々は「ブラウザで動くScheme」を夢見てたみたいだが、その後の方針変更で(C言語系構文の方がさすがにウケる、って言う事だろう・笑)、「Schemeを基礎としたような」ブラウザで動くスクリプト言語処理系を作成する。それが後のJavaScriptとなる。
そう、だからJavaScriptは本質的にはSchemeが関数型言語である程度は関数型言語で、名前にJavaが付く割にはJavaとは全然関係がない。むしろJavaより遥かに高機能なプログラミング言語だ。

ただ、JavaScriptは長い間「真面目な」プログラミング言語とは捉えられてなかった。一つの理由はいつぞや書いたが、「どう書けばキレイなJavaScriptコードが書けるのか」と言う指南書がなく、VisualBasicが歩んだ道と同様に「素人も簡単に書けます!」と言う宣伝ばっか先行してて普及したコーディングスタイルがメチャクチャだった事。
もう一つが、ブラウザに搭載される目的で開発されたスクリプト言語だったわけだが、「処理系同士」で長い間互換性が取れなかった事。あるブラウザAで動くコードをブラウザBで動かすとエラーが起きる、なんつー事が頻繁に起きてプログラマ泣かせだった、と言う事だ(大体がIE6を擁してたMicrosoftが違反してた・・・しかも違反してたブラウザが最大ユーザー数を誇ってたんでどうしようもなかった・※1)。

ただ、現在では尖った人たちはJavaScriptを「関数型言語」として正しく解釈してて、むしろOCamlのように「オブジェクト指向と関数型の両者の性質を併せ持った」優れたプログラミング言語として使っている。
僕自身は未読なんだけど、こういう書籍も販売されていて「より優れたJavaScriptプログラマになりたい」と言う層にアピールしてるらしい。

 
 
ただし、これはプログラミングに慣れた層にとっては、だ。
依然としてJavaScriptは絶対初心者が手を付けてはならない言語だと思っている。
理由は2つ程ある。
まず1つ目。
JavaScript、と言うかECMAScriptは仕様上入出力が無い言語だ。
そしてJavaScriptを使う環境はHTMLやCSSと併せて使う事が想定されている。大方、JavaScriptにとっての「端末」とはWebブラウザを意味する。
大体のケースに於いて、「ズブの素人」はHTMLやCSSの知識がない。つまり「JavaScriptからプログラミングを学びたい」と言った場合、HTMLやCSSを併せて学ばなければならないと言う事だ。
要するに「JavaScript"だけ"を学びたい」と言う事が通常は不可能なんだ。余計な知識をいっぱい覚えなければならない、と言う事はフツーのプログラミング言語に比べると学習は遥かに負担が大きい、と言う事を意味する。
テキトーでデタラメな奴らは「ブラウザで勉強出来るから簡単!」とバカな事を言うが、事実は逆だ。「ブラウザを使わないと学べない」のはプログラム初心者にとっては単なるリスクだ。
あと、正確な知識を得るのが不可能、たぁ言わねぇが、正確な知識を知らないヤツが多すぎる、ってのも欠点だ。これが2つ目。
以前「教えて!goo」で「console.logと言う命令を見てJavaScriptだ、と分からないなんて」とか言われた事があるが、そもそもconsole.logはJavaScriptの一部じゃない。むしろこれはブラウザ側の機能だ。
こういうのをWeb APIと呼んだりするが、実の事を言うと、「組み合わせるプログラミング言語」は別にJavaScriptとは規定されてないんだ。(他にあれば)どんなプログラミング言語に対してでも動くように設計されていて、従ってconsole.logが出てきたから、と言って「言語がJavaScriptである」とは決めつけられない。
こういうバカな発言が出て来る事で分かると思うが、いわゆる「フツーの」JavaScriptユーザーは結局「一体自分が何を使っているのか」分かってない。C言語脳に近い状態になってるわけだ。こういう輩を量産するのが良いことなのか?と言うと違うだろ。JavaScript、って名前を聞いてJavaの一種だ、って思い込むのと同じくらい酷いJSerを増やしてるんだ。
キチンと基礎からプログラミングを学ぶのに、環境を含めて向いてない、ってのがJavaScriptなんだ。JavaScriptは結果、プログラミング初心者にとっては複雑過ぎるんだよ。

さて、こう書いてみると

「スタンドアロンの処理系があれば解決すんじゃね?」

と思うだろう。
全くその通り、なんだが、その導入にせよ、フツーのプログラミング言語処理系に比べると手間だ、ってのが問題なんだよな。
有名なJavaScriptのスタンドアロン処理系には次のようなモノがある。

RhinoはJavaで書かれたJavaScript処理系で、現在は単独でも入手出来るし、あるいはJavaの開発キットに「Java用のScript言語」として付録として付いてくる(※2)。
これが入手が一番簡単なんだけど、残念ながら開発があまり活発ではないらしく、最新のECMAScriptの仕様を反映していない(と思う)。だから仕様改訂に拠る、よりシンプルなコードを記述するには向かないんだ。
SpiderMonkeyやGoogle V8は実際のブラウザで使われてるJavaScriptエンジンを単独で使えるように取り出したモノだ。SpiderMonkeyはC/C++で書かれていて、Mozilla Firefoxで使われているエンジン、Google V8はC++で書かれていて、Google Chromeで使われているエンジンだ。
両者共実際Webブラウザで使われているエンジンが故に、最新のECMAScriptへの対応が早い。
ただし、これらはインストールがメンドイ。ひょっとしたらビルド済みのバイナリも入手可能かもしれんが、基本的には「ソースからビルド」するようなカタチで提供されている。「ソースからビルド」だぜ?この言い回しが分からん、って人はそもそも入手せん方がいい。
結果、例えば「Pythonをダウンロードしてインストールする」とか、「Rubyをダウンロードしてインストールする」よか遥かに敷居が高いんだ。いや、プログラミングに慣れてる層だともちろん出来る、んだけど要はやっぱ初心者向けじゃねぇんだよ。プログラミングやった事が無い層が「ソースからビルド」なんざ出来るのか、っつーとまず出来ねぇだろ。
結果手間がデカすぎるんだ。

と言うわけで、特にWindows上だとスタンドアロンのJavaScript処理系を入手するのは「出来ないとは言わない」けど、かなり面倒臭いのが事実だ。
唐突に話はLinuxへと飛ぶ。Linux一般、っつーよりUbuntuだな。
過去のUbuntuのリポジトリにはSpiderMonkeyやGoogle V8が含まれていた。だからSynapticパッケージマネージャ辺りで検索して即座にインストールして遊べたモノだったんだが・・・・・・。ところが消えてから久しくなったんだよなぁ。いつでもインストールして使用可能なのはRhinoぐらいしかねぇ、ってトコだったんだ。
Google V8はともかくとして、SpiderMonkey辺りが恋しい・・・とか思ってたんだが、名前が変わってた、って事を最近発見した。
これが現行のSpiderMonkeyのUbuntu上の名称だ。

  • libmozjs-91-0
  • libmozjs-91-dev
この2つをインストールすればSpiderMonkeyを使えるようになる。
ややこしいのが、通常lib-って接頭語が付いてるのは文字通りライブラリとなってて、「それ単独で実行出来る」ようにはなっていない。
ところが、この2つは例外みたいだ。JavaScriptのスタンドアロン処理系が入ってる。紛らわしい。
起動コマンドは、上のヴァージョンだとjs91になる。



これでPythonやRubyのirbのように、端末で「新しい仕様に従った」JavaScriptインタプリタで遊べるようになった。
なお、Ubuntu上だとjsはRhinoの起動コマンドでjs91がSpiderMonkeyの起動コマンドだ、と言う事になってる。
また、上の動作例を見れば分かるが、console.logも使えるようになっている。過去のSpiderMonkeyやGoogle V8だと「独自実装」で、print関数なんかが搭載されていたが、いわゆる「フツーのJavaScript」のソースと互換性を高める為、console.logが追加されたんだろう(※3)。
いずれにせよ、プログラミング経験者向けで、「JavaScript”だけを”勉強したい」人にはこういうスタンドアロン処理系の情報は役に立つだろうし便利だ。一々チマチマとブラウザ開いて「ウェブ開発ツール」を辿って、間違ったHTMLのエラーを見てアタマに来ながらJS直打ち、等と言うバカな事をしないで済む。
JavaScriptのスタンドアロン処理系さえあれば、HTMLやらWeb APIに塗れなくて済む、ってのは大きいだろう。

なお、JavaScriptの例としてこれらの問題をJavaScriptで書くとどうなるのか、を示しておこう。

1. 3次元配列でドット絵を表示する

回答例:

実行例:


2. 配列の中身を一行ずつ表示する



回答例:

実行例:


3. 複数行テキストに行番号を追加する


回答例:


実行例:


さて、どうだろうか。
コード的には「ん?」とか思うかもしれないが、実は良く読んでみるとSchemeのお馴染みの関数の目白押しだ。
実際問題、JavaScriptと言う言語の特徴をPythonと比較すると「Pythonの組み込みの便利関数群よりはライブラリ関数群が少ない」が、一方、モダンな言語としては「Pythonより正しい」側面がある。Pythonは特にスコーピング等がデタラメだったりするが、JavaScriptはScheme由来の「正しい」仕様になっているし、Schemeでのお馴染みの関数がメソッドとなってたりするんで、Schemeの知識があれば比較的読みやすい。まぁ、C言語系の構文だけどな。
アロー関数式(=>)はギョッとするかもしれないが、これはいわゆるラムダ式を構成する書式だ。これが気に食わなかったら関数式を使っても良いが、どっちにせよ、Pythonのヘナチョコなラムダ式に比べると「正しく」「パワフル」だ。記述に慣れないウチは戸惑うだろうけど、Schemeを操るように書くことが出来る。
一方、JavaScriptにも特有の弱点がある。Pythonに比べると圧倒的にスクリプト指向なんだ。ここで言う「スクリプト指向」と言うのは、HTMLと組み合わせる前提の為、文字列処理中心に「考えすぎ」な仕様になっている、と言う意味。と言うか、何でも文字列処理に直結するような仕様になっていて、プログラミング素人だと「思った通りになりやすい」のかもしれないけど、逆にプログラミング慣れしてると「思った通りにデータ変換されてるかどうか」分かりづらい形式になっている。
例えば、Pythonの場合、「文字列のリスト」をそのまま文字列にしたい場合次のように書く。


まぁ、これが「フツーの言語」の挙動だろう。
ところがJavaScriptだと


と、配列の要素の区分けである筈のカンマ(,)がまるで文字列の一部のように扱われるんだ。
これはいただけない。この挙動は、オーソドックスに文字列のリストを文字列に変換するtoStringメソッドでも引き継がれている。


誰もこんな風にして欲しい、と思わない筈だが、JavaScriptは速攻で文字列として「結果を反映させたい」仕様になってる為か、こういう、他の言語で考えるとおかしな挙動になっている。
このカンマを避ける為には、joinメソッドの引数に空文字列("")を与えないとならない。


この辺はホント、特にPythonに慣れた層は気をつけた方が良いと思う。比較的似たような用語を使ってる為、ハマる時は思いっきりハマるだろう。
JavaScriptはconsole.logなんかを使った際に、配列でさえ文字列みたいにして表示するんで、データ変換が性交成功したんだか失敗したんだかプログラムした側にも良く分からないような状態になる・・・まぁ、他の言語のように「厳密に型変換しました」とならなくても単純な文字列として出力されるのはHTML塗れの中では利点なのかもしれないけど、結構気持ち悪い(笑)。

PythonやLispと違って、出力関数(Web API)は配列を単なる文字列として出力しちまう。

全般的にこの辺の「データ変換」のいい加減さがあったり、「型の厳密性」をJavaScriptは持ってないんで、この辺だけはハマり易いんで気をつけた方がいいだろう。

なお、上の3番目の問題はJavaScriptはPythonで言うenumerateを持ってないんで、reduceRightを用いて再現した、だけの解となっている。
Racketで言うと、関数digit-value

(define (digit-value ch)
 (string->number (list->string `(,ch))))

と定義しておいて、

(define *text* '("吾輩は猫である"
       "名前はまだ無い"
       "どこで生まれたか"
       "とんと見当がつかぬ"))

(foldr (lambda (x acc)
   (if (number? acc)
    (format "~a:~a~%" (sub1 acc) x)
    (string-append
     (format "~a:~a~%" (sub1 (digit-value (string-ref acc 0))) x)
     acc)))
   5 *text*)

と書くようなモンだ。
JavaScriptの「型判定」だと、そもそもJavaScriptには「型」のデータ型がなく、判定で文字列を返す、と言う仕様になってる模様だ。
従って、型名が無い以上、文字列比較で型を判定するしかない、ってのもフツーの言語で言うと「アレ?」って思うトコではある。

※1: JavaScriptは登場から比較的すぐ、Ecmaインターナショナルの下で言語標準化が成され、正式名称をECMAScriptと言う。JavaScriptは従って、ECMAScriptのNetScape/Mozillaによる「実装名」だと言うのがホントのトコだ。
なお、ECMAScript第一版を策定した人が、またもやこの人、Schemeの生みの親(の1人)であるGuy Steele Jr.だ。

※2: 「Java用のScript言語」と表現したが、これがJavaScriptの語源ではない。勘違いせんように。ここで意図してるのは、Java仮想マシン(JVM)上で動いて、Javaのライブラリを自由に使えるスクリプト言語処理系として利用されてる、と言った意味だ。
何度か言うが、JavaScript自体はJavaとは全く関係がない。単に旧ネットスケープのマーケティング戦略で、「今度Sun MicrosystemsがJavaって言語を出すらしいんで、それにあやかろうぜ!」ってぇんでJavaScriptと名付けられただけ、だ。
実際は、JavaScriptの方がJavaの正式リリースよりは早く登場している。

※3: と言うより、おそらくだが、SpiderMonkeyのconsole.logprintへのエイリアスだろう。
  • Xでシェアする
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

最近の「プログラミング」カテゴリーもっと見る

最近の記事
バックナンバー
人気記事