なお、以上の解説はJavaの文法とコマンドや標準ライブラリ等を一応知っていることを前提(現行のScalaはなおJavaライブラリへの依存度が高くScalaだけで完結できる状態では無い。なお、Scalaのコンパイラ自体はJava1.4用のコードも吐けるが、標準ライブラリが多く1.5を前提としている)とし、その違いだけをとりあえずは書き留めるものである。もっぱら文法やライブラリ参照用であることを目指しているので、例や特長等は次のリンクを参照されたい(なおただし、原著者たちの配慮にもかかわらず、それらの例は関数型言語に関する事前の概要的把握無しではその意義等をなお理解し難いものに留まっているように思われるので、そちらに自信の無い方は上の私の概説ページ群に先に目を通しておくことをお勧めする):プログラミング言語 Scala Wiki(処理系付属のチュートリアル及び例題集和訳;ただし、これらは原著自体が少し内容が古く現在の言語仕様と合わない部分がある)、多忙なJava開発者のためのScalaガイド、Scalaアクター -- ショートチュートリアル
Scalaとは、Java(Script)の皮(Javaで型付けされたJavaScriptと言うべきか)をかぶったHaskell++であるところの、その原理においてまったくに新しい(JVMすなわちJava環境上で使えかつJavaのclassファイルを生成する)プログラミング言語である。
Haskellとは、ラムダ計算という形式言語に基づきつつその最高純粋性を指向する、要するに「究極の関数型言語」(ラムダ計算とはミニマムな関数の組み合わせでプログラミングの一切を表現できる、とする形式言語であり、ゆえにこの系の言語はすべて「関数型」と呼ばれる。Lisp特にSchemeとE(macs)lispが名高いが、Haskellはむしろ一見Python的ではある;同様な「インデントによるブロック化」を採用しているためだ)であるわけだが、しかしラムダ計算は「変数を一切使わずにプログラミングできる」ことから、外部に意図せざる悪影響を与えない・受けない「純粋さ」が評価され来たる分散平行コンピューティング(一台のスーパーパワーなコンピュータを作る代わりに、何十台何百台もの既存のコンピュータに実行を分担させよう、という、ようするに「コンピュータ分業」のこと;Webアプリにおける分散的処理やマルチプロセッサの効率的利用もこれによってこそ数学的厳密さに根拠しつつ展開できるのであれば、現在最も必要とされ熱心に研究されている技術である)向けプログラミングの導きの糸、として期待こそされ、残念ながらそれ自体はそのようなものの無い時代の生まれなれば、いかんとも埋め難いギャップが厳として横たわるのであった。
そのようなわけで、分散平行コンピューティングをハナから視野に入れたパイ計算という形式言語が新たに編み出され、これはこれ自体でラムダ計算を表現できることからその上位互換なものとして自らを誇りはすれ、その実装、すなわち実際に使えるプログラミング言語としての具現化は難航する。そんなうちに、OCaml等の非純粋関数型言語での応用的研究からより実際的なジョイン計算(結合計算)という形式言語が生み出され、JoCaml、Join Java、Cω等既存言語に組み込まれる形(CωはC#の派生)でさらなる前進が推し進められたのではあったが、こちらでもやはりなお、その言語の根本からジョイン計算に立脚する新言語の開発は難航したーー「Scala」という名の元に、世界初の、ジョイン計算に基づく「(充分に)実用利用可能な」言語(実用を元から目指さない純実験用としては、パイ計算に基づくPictとジョイン計算に基づくFunnel(Scalaの作者による、Scalaの前身)とが既に在る)として、私たちの前に立ち現れるまでは。
スクリプト言語に慣れ親しむ人々は、そこに同じような「軽さ」と同時に直感性に反する「不可解さ」をも見る;JavaやC#に慣れ親しむ人々は、そこに同じような「厳格さ」と同時にマルチパラダイムの権化であるかのような「ついて行きづらさ」をも見る;HaskellやOCamlに慣れ親しむ人々は、そこに同じような「数学性」と同時にその変数やオブジェクト指向の多用に「妥協的危うさ」をも見る。だがそれはすべて、Scalaが「まったく新しいパラダイム」に属する、要するに既存のプログラミング言語からの類推を、その拠って立つ原理自体において既に「超越」してしまっているためなのだ。一切をエポケーしてみれば、Scalaほど「簡単明瞭」で「親しみ易」くそれでいて「妥協しない厳密さ」を持ち合わせている言語は、私の知る限り未だかつて、無い。
その一見スクリプト言語的な軽さも、実務派をも安心させるオブジェクト指向な厳格さも、研究者を夢中にさせる関数的数学性も、すべてはその原理であるジョイン計算が図らずも見せる一面に過ぎない。その真の威力は、関数概念を(型変数によるロジックの型抽象化をキーに)クラス概念へと拡張してのそのいわゆるカリー化ーーボトムアップなクラス階層化を支援しつつさらなるコンポーネント化を推し進める、というところにこそあろう:だが、その類いのことはその道のプロに任せて、せいぜい日曜プログラマである私は、ここではスクリプティングへの応用からJava代替(いくらかでも)簡易言語辺りまで、の紹介的解説に留めたい、と思う(と言うか、ハイレベルな研究者や実務最先端のエンジニアにこそ熱く注目されているScalaで今一番欠けているのがその辺りに関する情報なのであって、その意義ゆえにたかが日曜プログラマに過ぎない私が(英語マニュアルを読める、という特技ゆえに)あえて一石を投じん、という次第)。
Scalaはその記述の柔軟さと簡素さにおいて非常にしばしばGroovy、JRuby、Jython(とRhino)等の動的スクリプト言語と比較されがちではあるけれども、はっきり言ってそれはナンセンスである。と言うのは、Scalaは自身をも記述できる言語であり、そして実際にScalaの処理系は100%すべてScalaで記述されている(コンパイラは202ファイル61312行、ライブラリは474ファイル31039行(と18ファイル2342行のJavaソース;Java基本型との互換変換用とActorのスレッド処理部分のみ)、DBCライブラリは65ファイル2690行、デコーダは16ファイル2295行;なお、以上は自作のScalaスクリプトで割り出した)。これが意味すること、それは「GroovyもJRubyもJythonもRhinoもそれらの処理系自体をScalaで記述可能」である、ということであり、さらには本家バージョンアップへの追随性やより良きチューニング性(ともしかしたらさらなる高速性)のために、それらはむしろなおさらに「Javaよりもはるかにそれらに親和的なScalaでこそ再記述されるべき」である、ということである(バイトコード生成も可能な限りScalaのそれに一任するようにすればなお良かろう)。この意味で、Scalaと比較されるべきはむしろJava(もしくはC#)自体であり、さらにはCやC++こそがそのライバル、あるいは少くとも、Scalaに対し改めておのが立ち位置を見直すべき位置にある言語なのである。
実装云々以前に、言語仕様的にパフォーマンスを低下させる三つの要因がある:「関数遅延」、「式遅延」そして「型遅延」である。関数遅延とは、どの関数が呼び出されるのかが動的に決定されることだ。これは、「関数を選択する関数呼び出し」に等しく、この暗黙の二重関数呼び出しのゆえにパフォーマンスをいくらか低下させる(JavaのCとの速度差はこれに求められる)。式遅延とは、式の評価が動的に為されることで、これは要するに「インタープリタである」ことに等しい;ちなみに一般に言われる「遅延評価」はこの式遅延のことである(ML系でHaskellが最遅の原因をここに見出せよう。なお、極めてHaskell的ながらOCaml以上に高速なClean(残念ながら文字型が8ビットで日本語対応版は現在Windows用しか無く、そのため現状では日本で使われるにはプラットフォームに依存的過ぎ、またI/Oまわりが非純粋的なためにHaskell代替足り得るには至っていない)では、コンパイラが可能なところを可能な限り暗黙に非遅延化最適化する仕組みになっている)。型遅延とは、型決定が動的に為されること、すなわち動的型付けであることだが、これはその型すなわちクラスを評価毎に動的に再定義するに等しい;クラス定義は多数の式より成るのだから、これによるパフォーマンスの低下はこれら三つの中では最大である。Ruby(系)が際立って遅いのは言語仕様的にこの三つを全面展開せざるを得ないものだからで、ゆえにそれがそうである限りは、Scalaで記述し直しても高速化の方はあまり期待できないことになる(他の動的型付け言語も同様で、ゆえに高速化に関しては「もしかしたら」と弱い可能性を提示するに留まらざるを得ないわけである)。
Scalaの、そのような一見動的スクリプト言語的柔軟さと簡素さは、むしろOCamlやHaskellのような新関数型言語にこそ由来するものであり、たまたまこれらの中でScalaがJavaとの親和性への志向からそれら動的スクリプト言語にも最も似て見えるだけなのだ、とも言い得る。こちらとの比較で見れば、OCamlは偉大な始祖的位置にあるとは言え、しかしまたそれゆえにあまりの独自性(intが30ビットでfloatがスタックでは無くヒープに置かれる等)や妙な古めかしさ(charが8ビットでfor表現が貧弱である等)と未分化で未消化な独自用語の濫出(簡易オブジェクト指向的機能と本格的オブジェクト指向機能の並立による散逸的言い分け等;ちなみにこの並立ゆえに、OCamlでは本格的オブジェクト指向の方はそれほど重視されていない)が見受けられる。一方Haskellは、その純粋性ゆえにしばしば難解でかつまたオブジェクト指向とは原理的に相容れない。いずれ、Scalaはこれら新関数型言語の入門として最適(新関数型言語の難所である独自性や難解さはJavaのオブジェクト指向の拡張のようなものとして非常に分かり易く整理統合されており、また妙な制約や自己規制も最も少ないために主要概念や用法の学習に専念できる)であり、かつまたそれらとJavaとの橋渡しとしてもおそらくに有益((O)CamlもHaskellも登場してから既に20年以上経つが、未だにCやJavaとのインターフェース問題に苦慮している)である。
現在のところ、新関数型言語であるML系には三つの系統がある(ちなみにErlangは似ているがML系では無く、また動的型付けである)。本来のML系であり、ゆえにしばしばML諸方言の代表ともされるOCamlはそのMLから受け継ぐ性質として「非遅延評価」であり(遅延評価はライブラリによってサポートされるが、言語本来の機能では無いせいか評価実行を明示的に指示しなければならず面倒である)、一方Haskellは逆に遅延評価であることの貫徹にひたすらにこだわる(Haskellで最も難解と言われるモナドは、遅延評価では評価すなわち実行の時期が予測不可能であるために「実行順が重要」な一連の過程を取り扱えないのを、その過程を分離し遅延評価アクセスができるようにする仕組みなのであって、それ用演算子の読みづらさをも含めたその難解さはだから論理的必然よりもその遅延評価貫徹へのこだわりにこそ由来し、その意味ではHaskellは既に(純粋に計算機科学向け用途以外の応用実用向けとしては)破綻しつつある言語とも見なせるかもしれない)。非遅延評価と遅延評価は、本質的には同期と非同期に等しい;これはまた因果の時間細分と空間細分にも等しく、およそ人の言語思考はその両者の間のレミニスカートな輪廻より成り立っているのだから、どちらかだけに偏向することはその分だけ人の言語思考から逸脱してしまうことを意味する(この偏向はちょうどOCamlのような煩雑さやHaskellのような難解さを帰結する;なお、Haskellでも式自体は「順が重要」なのだから、関数内部は非遅延評価的なのであり、やはりその遅延評価へのこだわりは既にして(ここではさらに計算機科学向けとしてさえも)破綻気味であるようにも思われる)。そんなわけで、Scalaでは基本は非遅延評価ながら、遅延評価も言語本来の機能として自在に取り扱えるようになっている(このことと、Java由来の「一貫した可読性」とによって、Scalaではどれほど難しいことをしていても、その概念的把握の難しさによらず、OCamlのように煩雑になることも無く、またHaskellのように記述自体が難解になることも無く、その記述表明自体はなお明快な一貫性を保持し得ている;その記述表明そのものが、それが記述する一見難解な概念の最も簡潔にして最も正確な「辞書的定義」足り得ているのだ。だから、ScalaはOCamlやHaskellとまったく同じことができるにもかかわらず、いかなる特殊用語も特殊概念もそのために必要としない(例えばScalaのfor式は実際上Haskellのモナドに等しい仕組みだが、Javaのfor文の類推で何の難しさも無く理解できるようになっている);Scalaによる記述自体がその何であるかを既に語り尽くしているからだ)。
関数型言語の入門はしばしば再帰関数を使ったクイックソートを提示するのだけれども、よく見るとそれは二つのアトラクタと一つの特異点(収束点)から成る複雑系モデルそのものでもあることに気づく。次がScalaでのクイックソートだ。
def quicksort(x: Array[Int]): Array[Int] = if (x.length <= 1) x else { val 基準値 = x(x.length / 2) Array.concat( quicksort(x filter (基準値 >)), x filter (基準値 ==), quicksort(x filter (基準値 <))) }
集合を任意の値で「より大」と「より小」の二つに分け、この分割を再帰的に適用する;再帰が完了した時点ではすべての値が自身「より大」と「より小」な値に挟まれ、すなわちソートが完了している。任意の値に等しいかまたは要素数が一つなら再帰はせずその要素自身を返す;再帰自体は後者に至るまでさらなる再帰をし、それから今度はヨーヨーのように巻き戻りつつ結果を回収していく。この、要素数が一つという特例が収束点で、それへの分岐を促す「より大」と「より小」の条件がアトラクタというわけだ。つまり、Lispのような関数型言語とは、本質的には「問題を複雑系として把握しそのままモデル化する」ものだ、ということになる;実際、ラムダ式は本質的に自己定義的、すなわち再帰的であり、そして再帰性は複雑系の自己フィードバック性と相通じる。そこで、そのような関数を「複雑系関数」とここでは名づけておきたい。いずれ、Javaのような命令型言語による問題の把握とモデル化(ブロック分割による対象の静的な構造決定)に慣れた者が、関数型言語で最初に戸惑うのは、このような「問題の捉え方」にまったく不慣れであることによる。
そんなLisp系の欠点は、「部分的な複雑系関数はより大なる複雑系関数の部分としてのみ可能」だということ、つまり、複雑系関数同士の関係を有機的にしか表現できないところにある。現実の事象は、個々の独立的存在自体は一つの閉じた有機的システムでは確かに在れど、それらを項とする「出来事」そのものはそのような閉じたシステムでは無い。この時、その「場」を宣言的に特化することでその個的性質(すなわち「型」)を定められれば、それを外部的特異点として非有機的関係の複雑系関数群をまとめることができる。これが新関数型言語、すなわちML系であるOCamlとHaskellの本質だ(それらに特徴的な「パターン」はProlog的であり、すなわち宣言的である)。なお、オブジェクト指向もまた別な解決を模索するものではあったが(オブジェクト指向は関数型言語から始まった)、こちらは有機的統合をさらに推し進めるものであり、それによる「個性化」はしかしそれ自体を一つの静的に閉じた世界としてしまうことで、ライブラリ形成原理として以外の「汎用的」事象対応能力を喪失するに至った、と見なせよう。
しかしながら残念なことに、時代的制約もあって、それら新関数型言語はその本質において既に分散平行コンピューティングに対応していない、つまり、そのような「場」が非同期的に多数在るような状況(これをここでは「世界」と名づけよう)はまったく想定されていない。「世界」は動的であり、それを宣言のような静的定義で特化することには無理がある。ではどうするか。
「世界」そのものは動的でも、その項である各々の「場」自体はなお静的なのだから、宣言を「場を特化するもの」では無く、「場へ特化させるキー」と捉え直せば、「世界」をなお動的に把握しながらそれを宣言で静的に記述することが可能になる:これがジョイン計算とその具現であるScalaの本質だ。この「場へ特化させるキー」としての宣言は、どのような世界へも対応できるものであるから、これをホルダーとしての抽象的なオブジェクト指向「世界」へと使わない時には束ねておける。かようなキーリングの如き抽象「世界」は種・卵またはDNAのようなものとして捉え直せよう;現実の事象と接するや否やそれらは割れ開き巻きほぐれて展開し、そして再帰的に巻き戻りつつ必要な結果を回収する。この抽象性を具現するためにScalaの用いる方法論は、「プロトタイプ」による、スチームパンクな機械にも類せる「驚くほど簡素」な実装であり、そしてそれらを階層化することで自ずと成る「汎用スケルトン」である;実際、既に知られているJavaScriptのプロトタイプこそは「場へ特化させるキー」としてのクラスに他ならず、Scalaとの違いは、それを静的宣言としてインスタンスとは分離するかあるいは動的キーとしてインスタンス自体に織り込むか、だけである(なお、このために、ScalaではJavaその他のクラスベースオブジェクト指向言語に比べてはるかに気楽にクラスが作れる;実際、関数を作るノリでほいほいとクラスが作れてしまうのだ(と言うか、むしろ(見た目は)クラスに属さない独立関数が逆に不安を感じさせて作りづらいほどである))。
こんなわけで、PythonやJavaがブロック的、Ruby(とGroovy)やPerlがストリーム的である時、またLisp系がフラクタル的、ML系がローカル組織的である時、Scalaは「スクロール」的に問題を把握しモデル化する;ここで言う「スクロール」はその原義である「巻き物」のことで、パックされたデータを展開する過程(非同期なやり取りもこの一部である)がそのまま問題のモデル化であり、そしてそれを再パックする過程がそのまま解すなわち入用な情報の獲得となる。すなわち、データ自体が本質的にフラクタルな構造と仮定され(これは断続的ストリームとも見なせよう)、おのがローカル組織であるクラス階層と照会しつつその組織化されたコピー(これはストリーム化されたブロックと見なせる)を生成し処理する。個々の処理はフラクタルなので結果は本来のフラクタルな構造の部分をさらにフラクタル展開させつつ再合成したものとなる;このようにして、Scalaは情報にさらなる自己照覧的情報を繰り込むことでその「有用性」を、あくまで非同期な動的ネットワークの制約の中で、増大させるのである。