「プログラミング言語理論」という研究分野がある。この分野の研究者たちは,「ML」「Haskell」「Scheme」あるいは「λ計算」「π計算」(円周率計算のことではない)など,多くのプログラマにとっては聞いたこともない言語やモデルについて,日夜研究している。そのような言語は,C,C++,Java,Perl,Rubyなど,今の世の中で広く使われている言語とは見た目や考え方が違いすぎて,「難しい」「役に立たない」などと思われがちだった。

 ところが,その状況が最近になって微妙に変化している。

  • HaskellやLispなど「関数型言語」についての記事が,専門の学会誌ではなく,一般の技術誌に掲載された(日経ソフトウエア2006年6月号「Haskellによる関数型プログラミング入門」など)
  • 様々なフリーソフトウエアがML,Haskell,Schemeなどで開発された
  • プログラミング言語の研究者ではなく,普通のプログラマのためのHaskellの解説書が出版された(「入門Haskell―はじめて学ぶ関数型言語」(向井淳著,毎日コミュニケーションズ発行),「ふつうのHaskellプログラミング ふつうのプログラマのための関数型言語入門」(青木峰郎著,ソフトバンク クリエイティブ発行))
  • Schemeを使用した,プログラミングの古典的な教科書である「計算機プログラムの構造と解釈」(ジェラルド・ジェイ サスマンほか著,ピアソン・エデュケーション発行)が再注目され,複数の読書会が開催されている
といった具合だ。

 なぜそのようなプチブームが起こっているのか? 「ソフトウエアの開発効率/品質問題」「CやC++以外の,JavaやRubyといった言語の普及」など,もっともらしい理由はいくつかあげられる。が,いずれにせよ,研究者たちは決して「役に立たない」「難しい」ことを目指してきたわけではなく,「よりバグの少ないプログラムを書ける」「より良いソフトウエアを作れる」ことを目指してきたのだから,その成果が広まるとしたら努力が報われるというものである。

 だが,もしこのプチブームがただのブームで終わってしまい,実際の内容が十分に理解されず,「やはり難しくて役に立たなかった」ということになっては残念だし意味がない。そこで,この連載では,微力ながら,プログラミング言語やソフトウエア科学の様々な研究を,できるだけ普通のプログラマやエンジニアにもわかりやすく(どちらかといえば理論よりも実用に重点をおいて)紹介させていただきたいと思う。私のような者が偉そうに他人の研究を紹介するなど勘違いも甚だしいのだが,もしかしたら何かの役に立てば…と願っている。

プログラミング言語「ML」

 連載のはじめの何回かは,「ML」というプログラミング言語を紹介してみたい。

 MLといってもメーリングリストではなく,XMLやHTMLのようなマークアップ言語でもない。英国エジンバラ大学(当時)のロビン・ミルナー氏らによって設計・開発された,プログラミング言語の名前である。ちなみに,ミルナー氏はコンピュータ・サイエンスのノーベル賞といわれるチューリング賞を受賞している。

 MLにはいくつかの方言や様々な実装がある。すべて紹介することはできないので,Comp.Lang.ML FAQなどを参照していただきたい。これらの中で,Standard ML(SML)と呼ばれる標準規格は(ANSI CやJavaなど他の言語仕様と違って)英語や日本語のような自然言語ではなく,一種の論理式によって厳密に定義されている点が際立っている。SMLには,Standard ML of New Jersey(SML/NJ),MLton(ミルティン)など多数の実装がある。日本で開発されているSML処理系(の拡張)であるSML#もある。「プログラミング言語Standard ML入門」(大堀淳著,共立出版発行),「プログラミング言語ML」(ジェフリー・D.ウルマン著,アスキー発行)といった日本語の文献も出版されている。

 Standard ML以外のMLとしては,フランス国立研究所INRIAのザビエ・ルロワ氏らが開発したObjective Caml(OCaml)が有名だ。日本では名古屋大学のジャック・ガリグ氏も主要開発者の一人である。OCamlは,SMLのように厳密な仕様はないが,

  • (他のML処理系に比べれば)環境が整備されていて,インストールが容易
  • ライブラリやアプリケーションが豊富(参考リンク
  • プログラムの実行が高速(参考リンク
  • メーリングリストなどのコミュニティが活発(参考リンク
といった特徴がある。この連載ではOCamlについて紹介していく。

OCamlのインストールと実行

 OCamlのバイナリやソースコードはWebページからダウンロードできる。もっとも,DebianやCygwinなど,主要なシステムでは最初からパッケージが用意されていて,インストーラから選ぶだけで済むことも多い。Mac OS X用やWindows用のパッケージも,上のページからダウンロードできる。もしパッケージがなくても,主なUNIX系OSなら,ソースコードを展開して「./configure」「make world.opt」「make install」の三つのコマンドを実行するだけでインストールできるはずだ(ちなみに,Emacs使いの人は,ソースコードのemacsディレクトリにあるREADMEファイルに従って,caml-modeもインストールするとよいかもしれない)。

 OCamlには,「対話環境」「バイトコード・コンパイラ」「ネイティブコード・コンパイラ」の三つのインタフェースがある。対話環境は,ocamlコマンドを実行すると起動される。あるいは,caml-modeがインストールされていれば,EmacsからM-x run-camlを実行してもよい。Windowsでは,デスクトップの「Objective Caml」のアイコンをダブルクリックすると,OCamlWinPlusというインタフェースが起動する。しかし,このインタフェースは率直にいって問題が多いので,かわりにコマンドプロンプトからocamlコマンドを実行することをお勧めする。あるいは,ocamlbrowserコマンドを実行し,そのメニューから「File」→「Shell...」を選択してもよい。

 OCamlの対話環境を起動すると,

> ocaml
        Objective Caml version 3.09.1

# 

のような画面になるはずだ。そこでおもむろに「1 + 2 ;;」と打ってEnterキーを押すと,

# 1 + 2 ;;
- : int = 3
# 

となる。この「1 + 2」がOCamlのプログラム(式)だ。それを計算(評価)したら,整数型(int)の「3」という結果(値)になった,というわけである。試しに同様の式を評価してみると

# 3 - 4 * 5 ;;
- : int = -17
# 1000 / 6 ;;
- : int = 166

となる。ちなみに,式の後ろにつける;;は「入力の一区切り」を表す合図であって,式の一部ではない。

 式の字句と字句の間には,空白や改行やコメントをいくら入れてもよい。コメントは(*で始まり*)で終わる。もちろん,式に括弧をつけてまとめることもできる。

# (3 - 4)
  * 5 ;;
- : int = -5
# 3 - 4 (* 中途半端なところで改行するとわかりにくい *)
  * 5 ;;
- : int = -17

 OCamlを終了するには,通常のUNIX環境なら,Ctrl + d(Ctrlキーを押しながらdキー)を押せばよい。WindowsのコマンドプロンプトならばCtrl + zである。
 また,よくわからなくなったら,とにかくCtrl + c(EmacsではC-c C-c)を押せば式の入力や評価を中止できる。

もう少し面白い例

 さて,電卓のような計算だけではつまらないので,もうちょっとプログラミング言語らしい例も試そう。

 OCamlの式には(Haskellと違って)「副作用」がある。つまり,式を評価すると,結果の値だけでなく,それ以外の効果を発揮する場合がある。例えば(意味はいずれ説明するとして)とりあえず「Printf.printf "Hello, world!\n%d\n" 123 ;;」という式を評価すると,

# Printf.printf "Hello, world!\n%d\n" 123 ;;
Hello, world!
123
- : unit = ()
# 

 となる。返り値は「ユニット」と呼ばれるダミーの値()だが,副作用として画面に文字列が出力される。また,「read_int ()」という式を評価し,例えば12345と入力してEnterキーを押すと

# read_int () ;;
12345
- : int = 12345
# 

のように式の値が12345になる。

 グラフィックスやネットワーク・プログラミングも簡単だ。式の意味の説明は後回しにして,とにかく

# #load "graphics.cma" ;;
# Graphics.open_graph "" ;;
- : unit = ()
# Graphics.draw_circle 100 100 50 ;;
- : unit = ()
# 

とすれば「Caml graphics」というウィンドウが開いて円が書ける。また,

# #load "unix.cma" ;;
# open Unix ;;
# establish_server
    (fun inchan outchan ->
      print_endline (input_line inchan);
      close_in inchan)
    (ADDR_INET(inet_addr_any, 12345)) ;;

とすれば,クライアントから文字列を受信して表示するサーバーができる。試しに上のプログラムを実行している状態で,別のocamlで

# #load "unix.cma" ;;
# open Unix ;;
# let (ic, oc) = open_connection (ADDR_INET(inet_addr_loopback, 12345)) ;;
val ic : in_channel = <abstr>
val oc : out_channel = <abstr>
# output_string	oc "Hello, OCaml!\n" ;;
- : unit = ()
# close_out oc ;;
- : unit = ()

というクライアントの式を評価すると,クライアントの送信した文字列"Hello, OCaml!\n"がサーバーに表示されるはずだ。ちなみにGraphicsUnixなどはOCamlのライブラリ(モジュール)なのだが,勘の良い人なら,上の例とOCamlのマニュアルのPart IVを見れば,かなりのOCamlプログラミングができるだろう。ちなみに,Unixモジュールは,Unixという名前だがネイティブのWindows(Win32)でも一部の機能を除いて利用できる(残念ながらestablish_serverは利用できないようだ。もちろん,Cygwinならば問題はない)。

 なお,OCamlがバイトコードのダイナミック・ローディングをサポートしていない環境では,上の例は「#load」の行がうまく動かないかもしれない。その場合は,

ocamlmktop unix.cma nums.cma str.cma graphics.cma
 bigarray.cma -o myocaml.exe

というコマンド(2行になっているが実際には1行)を実行すれば,あらかじめ無難な標準ライブラリをすべてリンクした対話環境「myocaml.exe」ができるので,それを利用すればよい。

OCamlの「型システム」

 さて,今までの例には整数や文字列等しか出てこなかったが,OCamlにはもちろん浮動小数もある。ところが,例えば「半径3の円の面積を計算しよう」と思って「3 * 3 * 3.14」を評価すると,

# 3 * 3 * 3.14 ;;
Characters 8-12:
  3 * 3 * 3.14 ;;
          ^^^^
This expression has type float but is here used with type int
# 

とエラーになってしまう。これはなぜだろうか?

 一般に,ML(やHaskell)は「型」についてとても厳格な言語である。例えば,OCamlでは,浮動小数と整数は完全に区別されていて,「3」と書いたら整数にしかならない。浮動小数にしたければ「3.0」あるいは「3.」と書かなければならない。それどころか,掛け算の「*」すら整数専用で,浮動小数の掛け算は「*.」なのだ。

# 3.0 *. 3.0 *. 3.14 ;;
- : float = 28.26
# 

 同様に,浮動小数の足し算,引き算,割り算は,それぞれ「+.」「-.」「/.」である。これは(少なくともOCamlに慣れるまでは)誰しも「うっとうしい」と思うだろう。

 しかし,よくよく考えると,実は,整数と浮動小数を混同してしまうほうが,一般にはずっと面倒である。例えば,誤差のない整数が「いつのまにか」誤差のある浮動小数になって,それを等号(言語によって=ないし==)で比較していたりしたら,ごく稀にしか起こらないため再現しづらいバグになり,悩まされるおそれが大きい。ゼロ除算などにより,結果がnan(not a number)や±inf(正と負の無限大)になる場合も厄介だ。それらを比較した結果についてのIEEE規格を正確に記憶している人間はどれぐらいいるだろうか? 以上のような理由から,SMLにいたっては,浮動小数を等号で比較することすら禁止されている(どうしても比較したい場合のためにReal.==という演算は用意されているが)。

 このように,MLには「一見すると融通が効かないように思えるが,実はたちの悪いバグを防ぐという点では強力である」という特徴が多い。今回は大まかな導入だけで終わってしまったが,次回以降,そのようなOCamlの様々な機能について詳しく見ていきたい。

著者紹介 住井 英二郎(すみい えいじろう)

 東北大学 大学院 情報科学研究科 助教授。最近,学部の授業で少しだけMLを教えるチャンスがあったのですが,限られた時間にしては悪くない反応で,ありがたい限りです。命令型言語の考え方に慣れてしまった人よりも,初心者のほうが関数型言語の簡単さを理解しやすいのかも…?