はじめに
この記事は、Sansan Advent Calendar 2017 の 16 日目として書かれたものです。去年は 11 日目の記事として Ada を取り上げました。今年は Mozart/Oz です。
Mozart/Oz とは?
Mozart/Oz、それは Mozart なのか Oz なのか、あるいはサリエリ1に毒殺されたモーツァルトが怨みの底から蘇り、オズ2として世界を支配しようとする物語なのか、わたしも調べてみるまではナゾにつつまれたコトバだなと思っていました。しかし調べてみると、実はこれはプログラミング言語の名前なのでした。どうも Oz というのが言語の仕様を指し、Mozart がその実装の名前なのだということだそうです。
なぜ Mozart/Oz なのか?
なんでも、いま原宿の女子高生の間で大人気だという『コンピュータプログラミングの概念・技法・モデル』という本3のなかで、プログラミング言語のパラダイムを紹介するのにつかっているのが、Mozart/Oz なのだそうです。
ここで言っているパラダイムというのは、たとえばオブジェクト指向だとか、関数型プログラミングといった、その言語が戦略的にそなえた構造的な特性のことです。Mozart/Oz は「マルチパラダイム言語」を謳っている言語なので、さまざまなパラダイムが組み込まれています。ですので、プログラミングパラダイムをテーマとした教科書で扱うには都合がよいのかもしれません。
もちろん、C 言語でもオブジェクト指向のようなプログラムが書けるというように、言語にあらかじめ盛り込まれた概念を超えて、パラダイムを表現するということは不可能ではないでしょう。しかし、言語にあらかじめ盛り込まれているならば、そのパラダイムはきっと簡潔にわかりやすく表現できるでしょうし、そのコードを動かしてみれば、そのパラダイムの理想の動き(のひとつ)がどういうものなのか把握しやすいのではないかと思います。ですから、よく設計されたマルチパラダイム言語の言語仕様に触れることは、それぞれのパラダイムを理解することにつながるのではないかと期待できるわけです。
残念なお知らせ
Mozart はオープンソースで公開されていますが、2017年年末現在、どう見ても開発は止まっているようです。2013 年に書かれた Wiki ページでは Mozart 2.0.0 alpha 1 が「very soon!」(間近!)であり、Mozart 2.0.0 stable が「in a few months」(数ヶ月以内)とアナウンスされていますが、2017 年年末現在でも入手できるのは beta.0 と呼ばれる版のままです。
Mozart は 1.x.x 系と 2.x.x 系の 2 つがあり、入手しようとすると基本的に 2.x.x 系へとナビゲーションされるようになっています。しかし 2.x.x 系は beta のままです。しかも、わたしは Arch Linux4 に 2.0.0 beta.0 をインストールしてみたのですが、少なくとも README に書かれた手順では標準ライブラリのいくつかはなぜかインストールされません。これはバグなのか beta の仕様なのかは不明ですが、Mozart に実装されているといわれているすべての機能がつかいたい場合には 1.x.x 系を利用するほうが無難である可能性があります。この記事も、1.4.x を前提に書かれています。
インストール
前項で書いたとおり、ふつうは 2.x.x 系をインストールするようにナビゲートされると思います。2.x.x 系の場合注意する点は Java のバージョンです。README にはバージョン 1.6.0 以上の Java が必要とだけ書いてありますが、Java 9 ではインストールでエラーになるだろうと思います。わたしは 2.x.x も入れましたが、そのときは Java 7 を使いました。Arch Linux は AUR5 から複数の OpenJDK を自由に入れられるのでこういうときに便利です。
ただ、この記事は 1.x.x 系をつかうことが前提になっています。2017 年現在、1.4.99 が最新です。1.4.x の場合、Ubuntu か Debian にインストールするのが都合がよさそうです6。わたしは VirtualBox に Ubuntu 16.4 をインストールしてそこに入れることにしました。
基本的には README に書かれた手順どおりにインストールすればよいのですが、libgmp-dev は i386 ではないものが必要になります。また、分散プログラミングを試す場合は、リモートホストへのアクセスに ssh をつかいたくなると思います。ですので、ホームディレクトリ配下にインストールすると若干ですがやることが増えそうです。つぎのように手順を変更してもよいかもしれません。
$ sudo apt-get install emacs flex bison tk-dev build-essential g++=multilib zlib1g-dev:i386 libgmp-dev git
$ mkdir ~/works && cd ~/works
$ git clone git://github.com/mozart/mozart.git
$ cd mozart
$ ./configure --prefix=/usr/local/share --disable-contrib-gdbm --enable-modules-static
$ make
$ sudo make install
$ for path in /usr/local/share/mozart/bin/* ; do sudo ln -s ${path} ${path/share\/mozart\//}; done
なお、Mozart には OPI(Oz Programing Interface)というものがあります。これは Emacs7 を利用した Mozart の開発環境なのですが、GUI 版 Emacs のマルチウィンドウが前提になっているので、インストールする OS は GUI がつかえるようになっているほうが便利です。もっとも、必須というわけではありません。
Hello world
もし OPI をつかわずに、ふつうのプログラミング言語のように自由なエディタでコードを書き、コンパイルしたいと望んでいるなら、Hello world をはじめるには Mozart のドキュメントを探すより The Hello World Collection に掲載されたコードを見るのが手っ取り早いでしょう。説明の便宜も含めてここにコードを引用します。
% Hello World in Oz
functor
import
System
Application
define
{System.showInfo "Hello World!"}
{Application.exit 0}
end
コンパイルするには次のようにします。
$ ozc -x helloworld.oz
実行ファイルはそのまま実行できると思いますが、Mozart 2 ではエラーになるかもしれません。その場合はつぎのようにすれば実行できるかもしれません。
$ ozengine helloworld
OPI をつかわない Hello world のプログラムはそれなりに長いものになりました。しかし、OPI をつかえばもっとお手軽になります。まず oz
コマンドで OPI を起動してみましょう。
$ oz &
たちあがった Emacs には 2 つのバッファが表示されています。ひとつは Oz
というバッファです。*scratch*
8 バッファのように、こちらでいろいろコードを書いて試せます。もうひとつは *Oz Compiler*
というバッファで、こちらにはコンパイルのログが表示されます。
さて、Oz
バッファにつぎのように書いて、C-. C-b
9 とタイプしてみましょう。
{Show 'Hello World!'}
すると *Oz Compiler*
バッファのほうにログとしてコードがそのまま表示され、つづいて accepted
と出たと思います。そうなっていれば成功です。C-. e
をタイプして *Oz Emulator*
バッファに表示を切り替えて出力結果を確認してみましょう。なお、もとの *Oz Compiler*
バッファに戻すには C-. c
とタイプします。
なお、ここでは公式チュートリアルにならって {Show ...}
というふうにコードを書きましたが、これは {System.show ...}
と書いたのと同じ意味です10。最初の Hello world サンプルでは show
ではなく showInfo
をつかっていましたから、完全に対応させるなら次のように書くことになります。これももちろん先ほどと同様の操作で実行することができます。
{System.showInfo "Hello World!"}
さて、OPI の例としてはよりお手軽な Oz Browser についても触れておくべきでしょう。これは GUI 版の OPI でなければつかえないと思いますが、便利なので紹介します。Oz
バッファのコードをつぎのように書き換えて C-. C-b
を実行してみてください。
{Browse 'Hello World'}
Oz Browser が別フレームで現れ、そこに出力結果が表示されたはずです。
さらに詳しい情報については Mozart/Oz のチュートリアル11を参照してみてください。
その他コード例
Mozart/Oz の英文のドキュメントにはさまざまなコード例が書かれていますが、全部読むのには骨が折れます。ざっと見渡して試してみるには、Wikipedia くらいの簡単な例がよいかもしれません。いくつか見てみましょう。
関数
declare
fun {Fact N}
if N =< 0 then 1 else N*{Fact N-1} end
end
X = {Fact 10}
{Show X} % => 3628800
OPI でのコード例です。Wikipedia の例はコード片のようなものも多いようで、そのままペースとして試そうとしても動かないことがあるかもしれません。適宜 declare
などを書き足して文法に適合させる必要がありますので、慣れないうちは注意が必要になります。
上記の例はいわゆる階乗(Factorial)の例です。Oz の文法をしらないとしてもプログラムの内容は見当がつくと思います。なお、Oz では識別子は大文字で始める必要があります。変数名が定数名のように大文字なのはそのためです。C 言語流の記法なら fact(n)
のようになるところが {Fact N}
のようにカッコの囲み範囲が Lisp 流(?)になっているのも特徴的かもしれません。
なお、コードの記法としては下記のように改行やスペースをつけても問題ありませんが、引数の区切りもスペースなので、演算子などはつなげて書いてしまったほうがわかりよいのかもしれません。コード例ではたいがいそうなっています。
declare
fun {Fact N}
if N =< 0 then
1
else
N * {Fact N - 1}
end
end
パターンマッチ
Oz には case
をつかったパターンマッチがあるのですが、表記方法はちょっと独特な感じがします。
declare
fun {SumList List}
case List of nil then 0
[] H|T then H+{SumList T}
end
end
X = {SumList [1 2 3 4 5]}
{Show X} % => 15
文法はつぎのようになっているそうです。E
の値が Pattern_1
に合致すれば S1
が実行され、Pattern_2
なら S2
、というふうになります。
case E of Pattern_1 then S1
[] Pattern_2 then S2
[] ...
else S
end
2 つめ以降のパターンをかく場合には []
をつけてつなぐわけですね。この []
という表記へのとまどいと、それに続く H|T
がリスト(配列)を表す12ことがわかれば、あとはなんということもないと思います。念のため、上記のコードをなるべくそのまま Ruby で書いてみましょう。おそらく、つぎのようになる感じでしょうか。
def sum_list(list)
if list.first.nil?
0
elsif list.size >= 2
list.first + sum_list(list[1..-1])
end
end
x = sum_list([1, 2, 3, 4, 5, nil])
puts x
再帰呼び出しを伴っているので、Ruby で書いても若干わかりにくさはあるかもしれません。もちろん Ruby 本来の書き方ならもっと簡潔になるわけですけれども。
puts [1, 2, 3, 4, 5].inject(&:+)
データフロー
ここで言っているデータフローというのは、値に束縛されていない変数があると処理を行わずに束縛されるのを待つというものだそうです。要するに値がわからない変数があれば値が入るまで待つということでしょう。
Wikipedia にはちょっと興味深いコード例があります。すこしだけ数値を調整して試してみましょう。
declare
fun {Ints N Max}
if N == Max then nil
else
{Delay 1000}
N|{Ints N+1 Max}
end
end
fun {Sum S Stream}
case Stream
of nil then S
[] H|T then S|{Sum H+S T}
end
end
local X Y in
thread X = {Ints 0 10} end
thread Y = {Sum 0 X} end
{Browse Y}
end
最後のほうの thread X = {Ints 0 1000} end
のところの数値が Wikipedia では 1000
ですが 10
に変更しています。これは Delay
によってスリープするようになっているので、時間がかかるからです(C-. h
で Halt してしまってもいいのですが)。
実行してみると Oz Browser に Y
のリストが表示されだします。
最初は何もないリストに徐々に値が入っていきます。これ、どういうしくみなのでしょうか?
最初の関数 Ints
は単に 0 から 10 までのリストをつくる関数です。ただし、スリープしながらすこしずつリストを伸ばしていきます。2 番目の関数 Sum
は、要するに出力結果のような漸化式的な数列をつくっています。
最後に 2 つの thread
をつくっていますが、X
の値が追加されると Y
のほうの計算も追加された分だけ続行され、Oz Browser にそれが表示されるというしくみのようです。
Mozart の thread
はコストが低いそうですから、試みにもうひとつ足してみましょうか。
declare
fun {Ints N Max}
if N == Max then nil
else
{Delay 1000}
N|{Ints N+1 Max}
end
end
fun {Sum S Stream}
case Stream
of nil then S
[] H|T then S|{Sum H+S T}
end
end
fun {Fact S Stream}
case Stream
of nil then S
[] H|T then S|{Fact H*(S+1) T}
end
end
local X Y Z in
thread X = {Ints 0 10} end
thread Y = {Sum 0 X} end
thread Z = {Fact 0 X} end
{Browse Y}
{Browse Z}
end
これで X
が増えるたびに Y
と Z
の計算が続行されるということになります。
このように、複数の thread
をつかって協調的に処理をすすめることもできるわけですね。
おわりに
Mozart に組み込まれた機能について簡単に紹介してみました。しかし、ここで紹介できたのはほんの少しです。ほかにも、たとえば Oz らしい分散プログラムについてなども紹介してみたかったのですが、長くなってきてしまったのでここで筆を擱くことにします。
* * *
さて、ここでちょっと歴史的なことに視線をなげてみましょう。代表的な言語があらわれて以来の、われわれの環境の変化を振り返ってみるのです。すると、マルチコア、Cloud、ビッグデータ、機械学習、などなどけっこう大きく進化しているのではないでしょうか。そうした進化によりよく対応したパラダイムというものは、もしかしたらまだまだ言語に盛り込む余地があるのかもしれません。もしそうしたものが盛り込まれるなら、プログラミングはいっそう魅惑的な「魔術」にちかくなっていくのではないでしょうか。それは見方によれば Oz のような名前をつけるのにふさわしいようなことかもしれません。その実際の使い手の正体が平凡な人であるかもしれないこともふくめて。
-
アントニオ・サリエリ Antonio Salieri(1750 - 1825)。元ネタは言わずと知れた映画『アマデウス』(1984年)。 ↩
-
ライマン・フランク・ボーム作『オズの魔法使い』に出てくる魔法使いを指している。そんな話じゃなかったけ? ↩
-
ピーター・ヴァン・ロイ、セイフ・ハリディ著。原題 “Concepts, Techniques, and Models of Computer Programming” 翔泳社から日本語訳が出ている。 ↩
-
Arch User Repository のこと。https://aur.archilinux.org/ ↩
-
README には Arch Linux のインストール手順があり、そちらのほうが圧倒的に簡単そうだが、手順で書かれている AUR パッケージはすでに存在していない。別の Mozart パッケージもあるがインストールできないようだ(Mozart 2 のパッケージはインストールできると思うが)。 ↩
-
GNU Emacs https://www.gnu.org/software/emacs/ ↩
-
簡単にいうと、お手軽に Emacs Lisp が動かせるようになっているバッファ。 ↩
-
Emacs のコマンド表記では
C-
で Control キーを押すことを意味する。 したがってC-. C-b
は Control キーを押しながら「.」キーを押し、Control キーは押したまま「b」キーを押すことを意味する。C-. c
のように、2 回目のキータイプでは Control キーを離すということもある。 ↩ -
Oz のリストはいくつかの表記方法がある。
1|2|3|nil
のように書くこともできるし、[1 2 3]
のようにも書ける。さらには'|'(1 '|'(2 '|'(3 nil)))
のように書くこともできる。最後の表記が表しているとおり、基本的に Oz のリストは Lisp のようにペアでできているらしい。パターンマッチのH|T
はおそらく Head と Tail の意味で、このときの T は先頭要素以外のすべての要素を指す。 ↩