まぁ、最後のは J 師が激昂して叫んだ言葉ですのでアレであるが、かように今現在、ワタクシの周囲では関数型言語という言葉が飛び交っているのである。と言っても騒いでるのはワタクシだけだが。他の人にとっては常識なのかも知れぬ。ちょっと焦るのである (冷汗)。 1996 年 3 月には、ある意味でもっともメジャーな ML の本 Jeffrey D. Ullman, "Elements of ML Programming", 1994 の翻訳書 [*3] が出たんで、ハマっている方もおられる方も多いかと思われる。ちなみに、ワタクシはこれに遅れること 3 年半。ようやく 1999 年の 10 月に至って読んだ。ほんまに冷汗モノであることよ。
「関数プログラミング」ってなに? 「関数型言語」って、 C (とか) の「関数」と関係あんの?
関数プログラミングを一言でいえば、関数の定義と関数の呼出しだけでプログラムを書く、というものである。 ここでいう関数とは、 C などのプログラム言語でよく使われている「関数」ではなく、数学の関数である。 そこには、手続き型プログラミングに代表的な代入操作や、いくつかの文の逐次実行という概念はない。 むしろ、そのような言語の中の式だけでプログラムを書くというようなものである。
竹市正人, "関数プログラミング", bit, vol. 31, No. 3, 共立出版, 1999, ISSN0385-6984, p. 7.
ここでの関数とは、ある値を与えると関数の値として常に決まった値が返ってくる、領域の要素に対して値域の要素がただ一つ決まるような「対応の規則」のことですな。この「対応の規則」を組み合わせて目的とする動作を得るように書かれた「関数プログラム」を解釈実行するのが関数型言語。 「そんなことができるのか、また、なぜそれほどまでに無理をするのか」 (ibid, p. 7) とかいう話も聞こえてくるんだけれども、やっぱ、そうまでして得たいものがあるわけで、
形式仕様技術 (Formal Specification) はオブジェクト指向技術と異なり、数学を基盤とした「証明可能な」技術基盤の上に構築されてきた技術である。参照の透過性がある宣言的仕様記述を主として使う。 生命に関わる (Safety Critical) システム向きの技術で、そのようなシステムの構築ではオブジェクト指向より優れた面が多い。 形式技術は、仕様記述言語と開発方法論からなり、実装は、 参照の透過性 があり 副作用のない 関数型言語 で行うことが多い。
佐原伸, "デザインパターン オブジェクト指向分析/設計技法", SRC, 1999, ISBN4-88373-119-7, p. 202.
安全だという話はよく聞く。代入がない (必要悪としてなら存在する関数型言語もある) というのも、よく聞く。 代入が一般的、というより、一般的にそれがなければ手も足も出ない C, Pascal その他もろもろがおおきに使われている現世であるがゆえに、「関数プログラミング」「関数型言語」という言葉には、なにやら秘術的な雰囲気がまとわりつく。それだけに、「代入がない言語」というだけで、何やら妖しげな雰囲気が漂うのである。だって、あ〜た、メモリ管理不要、ポインタ不要ってだけで、拒否反応 (話を聞かない) を起こしてゲシュタルト崩壊するやつらがいるんだぜ。 おまけに、関数型言語の特徴となるキーワードは、やたら妖し気に聞こえるのが多い。だもんで、聴き手はますます引いてしまうのであ〜る。曰く、
ううむ、音だけ聴いてもエエ感じ (妖し気) だ。 実際には、ほんの少しかじっただけでも、「おお」とか「へぇ〜」とか (まぁ、「ズルいっ!」とかいうヤツもいるが) 感嘆詞が出てくるのが普通だけれど、先の 4 師が薦める意図というのは、案外この辺 (感嘆詞を出して、なおかつ、体感すること) ではないのか、という気もする。 だもんで、上記の聖言は自分で調べてくれ。やっぱ、「おお」とか「へぇ〜」とか「ズルいっ!」とか、感嘆詞を発語するだけでなく、体感せねばダメなのだ!
Pascal で計算機入門をした人は Pascal を究極的言語だと思いたがる。事実 Pascal はよい言語だから、ある程度まではそれでも大過ないが、計算機技術のほんとうのプロになりたいなら、そこで立ち停ってはこまる。 Pascal にも限界がある。 (中略) その限界がおのずと目に見えてくるだけの修養を積まねばならぬ。
木村泉, 米澤明憲, "算法表現論", 岩波書店, 1982,. p. 56.
上記の "Pascal" を自分が今使っている、惚れている、のめり込んでいる言語に置き換えてみれば佳い。
で。 まずは処理系なんだけれど、これがけっこう困るのであった。 Mac 用の環境って、あるにはあるんだけれど、 Concurrent Clean 以外にはフル・サポートっつーのが、なかなか見つからないですな。ほとんどがテキスト・ベースなのでありゅ。 以下の分類は FAQ for comp.lang.functional に従っており、よって Scheme も入っているのだ。 win, unix-box なら、もっといっぱいありゃ〜すんで、上記から辿るがよい。 おっと、ちなみに、選考基準は「フリー」または「準フリー (非商用ならフリーとか)」でありゅ。
次のサンプルを好きな (適当な) 関数型言語で書いてみるべし。
竹内郁雄, "初めての人のための LISP", サイエンス社, 1986, 1989., ISBN4-7819-0454-8, p. 4.(progn (defun f (x y z) (cond ((> x y) (f (f (1- x) y z) (f (1- y) z x) (f (1- z) x y) )) (t y) )) (f 12 6 0) )
Concurrent Clean しか試してないけど、まず間違いなく、プラットフォームを問わずに動く。 って、もちろんソースコード・レベルだけど。 ML 系はちょっと判らん (バイナリ互換あるような気がせんでもない) が。 やっぱ、気持ち佳い。
fun f(x, y, z) =
if (x > y) then
f (f(x - 1, y, z), f(y - 1, z, x), f(z - 1, x, y))
else
y;
f(12, 6, 0);
または
fun f2 x y z =
if (x > y) then
f2 (f2 (x - 1) y z) (f2 (y - 1) z x) (f2 (z - 1) x y)
else
y;
f2 12 6 0;
let rec f(x, y, z) =
if (x > y) then
f (f(x - 1, y, z), f(y - 1, z, x), f(z - 1, x, y))
else
y;;
f(12, 6, 0);;
または
let rec f2 x y z =
if (x > y) then
f2 (f2 (x - 1) y z) (f2 (y - 1) z x) (f2 (z - 1) x y)
else
y;;
f2 12 6 0;;
この程度のプログラムだと Moscow ML, Standard ML 93 のコードとほぼ同じ (fun と let rec, ; と ;; が違うだけ) である。
(define (f x y z)
(cond ((> x y)
(f (f (- x 1) y z)
(f (- y 1) z x)
(f (- z 1) x y)))
(#t y)))
(f 12 6 0)
MacGambit では、デフォルト・メモリ設定のまま、かつ、 trace を掛けていると OS を巻き込んで死ぬので注意。ウィンドウ下部のメモリ・バーがものごっつぅ伸縮しているのが見える。 700 段以上あるぞ。
f :: (Int, Int, Int) -> Int
f(x, y, z)
| x > y = f(f(x - 1, y, z), f(y - 1, z, x), f(z - 1, x, y))
| otherwise = y
または
f2 :: Int -> Int -> Int -> Int
f2 x y z
| x > y = f2 (f2 (x - 1) y z) (f2 (y - 1) z x) (f2 (z - 1) x y)
| otherwise = y
Hugs 98, MacGofer の場合は対話型とは言っても関数の定義はできない。 あらかじめファイルに定義して、それを読み込むことで使用できるようになる。 面倒だが、実行は 恐ろしく速い。激速と言っても佳い。ファイル編集の手間暇の時間を考えても、トータルの時間は短いかも知れない。
module mie
import StdInt
import StdEnv
mie :: Int Int Int -> Int
mie x y z
| x > y = mie (mie (x - 1) y z) (mie (y - 1) z x) (mie (z - 1) x y)
| otherwise = y
Start :: Int
Start = mie 12 6 0
括弧を付けて書くと……
module mie2
import StdInt
import StdEnv
mie2 :: (Int, Int, Int) -> Int
mie2 (x, y, z)
| x > y = mie2
(mie2 ((x - 1), y, z),
mie2 ((y - 1), z, x),
mie2 ((z - 1), x, y) )
| otherwise = y
Start :: Int
Start = mie2 (12, 6, 0)
標準の Concurrent Clean は、 Moscow ML や DrScheme, Hugs 98 などのようなシェル・タイプの対話型環境ではなく、 SuperCard (覚えてる?) や Delphi のような統合型の対話環境 (こんな用語があるんかどうかは知らん。今作った) である。 IDE から新規ファイル作って、入力して、これをモジュール名 + '.icl' で保存し、 run させると機械語まで落ちてプラットフォーム・ネイティヴなアプリケーションが生成されて実行されるのだ。 カレント・ディレクトリにサブディレクトリが掘られて、そん中にアセンブラのソースやらオブジェクト・ファイルやらが生成されるようになっている。 余談だが、 Concurrent Clean 本 (まだドラフト) には、対話型インタプリタを作るケース・スタディが載っている。
module mie3
import StdInt
import StdEnv
mie3 :: !Int !Int !Int -> Int
mie3 x y z
| x > y = mie3 (mie3 (x - 1) y z) (mie3 (y - 1) z x) (mie3 (z - 1) x y)
| otherwise = y
Start :: Int
Start = mie3 12 6 0
あと、こういう書き方も有効で、実は mie と mie3 は、意味的にたいへんに違うのである。 どこがどういう風に違うのか、というのは、まぁ、後のお楽しみということで。
| aBlock |
aBlock := [:x :y :z | x > y
ifTrue: [aBlock
value: (aBlock
value: x - 1
value: y
value: z)
value: (aBlock
value: y - 1
value: z
value: x)
value: (aBlock
value: z - 1
value: x
value: y)]
ifFalse: [y]].
^aBlock
value: 12
value: 6
value: 0
Object subclass: #MyRecursiveExample
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'MyExample'!
"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!
MyRecursiveExample class
instanceVariableNames: ''!
!MyRecursiveExample class methodsFor: 'examples' stamp: 'nishis 12/4/1999 18:20'!
doInsteadOfBlockWith: x with: y with: z
"MyRecursiveExample doInsteadOfBlockWith: 12 with: 6 with: 0."
^ x > y
ifTrue: [self
doInsteadOfBlockWith: (self
doInsteadOfBlockWith: x - 1
with: y
with: z)
with: (self
doInsteadOfBlockWith: y - 1
with: x
with: z)
with: (self
doInsteadOfBlockWith: z - 1
with: x
with: y)]
ifFalse: [y]! !
VisualWorks と同じようにやろうとしても、 Squeak では、まだブロックの再帰はサポートしていないので、 Attempt to evaluate a block that is already being evaluated. と怒られる。 面倒だけど、クラスを作る必要がある。いや、既存のクラスにクラス・メソッド一つ追加してもいいんだけどさ。
ML 系、 Scheme 系、 Haskell 系、 Concurrent Clean で、ほとんどソースは同じ、と言って佳いかも。 Haskell 系と Concurrent Clean にしたって、実質的に異なるのは Start 関数だけである。 モジュール宣言は Hugs でも構文はちょっと違うが使用可能 (たぶん MacGofer もそうだ)、インポート宣言も Hugs, MacGofer で構文も同じく使用可能。あとはまったく同じ。しかも Miranda にも似ているという、恐るべき相似性。 (ただし、 Concurrent Clean ではモジュール宣言して、適切なモジュールをインポートしないとまともに動かない。暗黙のインポートは「ない」みたい)
と、まぁ、いろいろな処理系があるわけであるが、 無難なところでは ML かな。浄く正しく、であれば Haskell (Hugs) なんて佳さそうだし。
ここではいっちょ Concurrent Clean でもつついてみようかいの、ということにしませうか。題して
Language Report の謳い文句には、 Concurrent Clean は
a general purpose, higher order, pure and lazy functional programming language based on graph rewriting designed for the development of sequential, parallel and distributed real world applications
であるとあり、 FAQ for comp.lang.functional でも、 Concurrent Clean の特徴として
などと揚がっているんだけれど、ナニ、「関数型言語」 or: 「関数プログラミング」って何それ? というレベルでは、
で、ぜんぜん説得力ないっすね (笑)。だいたい「関数型言語とは何か」、「関数プログラミングとは何か」という設問にすらすら答えられるようなら、こんなページ作らんし、読まないって。 むしろ 「Macintosh 上で」 という制限つきで
というのがいちばん大きいでしょう。 (今現在でも推薦していると思うんだけど、確認を取っていない)
みんな真面目なページなので、ここは一つ
ということで、 3 秒考えて、下世話な Concurrent Clean ページを作ろうと決めたのであ〜る。 「下世話」をキーワードにすれば、もし佐原さんがページを公開したとしても、まず間違いなくバティングしないはずだし。
ちなみに背文字は『プログラミング言語の最新トレンド』。をいをい、そりゃ違うだろうがという感じっす。でも、中身は佳い本だと思いますぜ。史上最強の ML は、高階関数、多相型 (polymorphism)、抽象データ型、強い型づけ、高度にパラメータ化されたモジュール機構、精密な例外処理など、プログラミング言語の最新トレンドを満載したプログラミング言語である。スタンドード大学のプログラミング教育用に採用された学びやすさを知ってもらいたい。
関数型言語ついに登場!
X-(
このページのタイトルは、 Led Zeppelin の大名曲のタイトルのもじりだが、参照してるのは 4th アルバムではなくて、 Frank Zappa のカバー版ライヴ・ヴァージョンである。同じアルバムに入ってる、これまた超有名な Cream のタイトルを拝借して "Functional of your Love" というのも考えたんだけれども。あと、 "In the Court of The Functional Programming" というのも浮かんだんですが、却下ですな、これは。
これの最後に入っている "Stairway to Heaven" を聴くと、まさに『悶絶』ものである。あの Jimmy Page のギター・ソロを、完全コピーで、しかもブラス・アンサンブルで演ってくれちゃっているのだ (しかも、これがめちゃくちゃ巧い!!)。おまけに基本リズムはレゲエ。必聴。
論理的にこの 上のページ タイトルの元ネタは、 (よせばいいのに) Hadfield and the North の大名曲 "Halfway Between Heaven and Earth" である。
Hadfield and the North は 2 枚のアルバムと 1 枚の編集アルバムを残して解散してしまった (ただし、 CD 化に際し、編集アルバム "Afters" の大半の曲は、ボーナス・トラックとして 2 枚のオリジナル・アルバムの余白に収められている)。この曲は、それらとは別の、レインボー・シアターの閉舘残念コンサートのオムニバス・ライヴに収められていたものである。スタジオ盤には収録されなかったけれど、 15 年近く経って突如出て来た一夜限りの再結成ライヴでも取り上げているように、お気に入りの曲だったようだ。実際、佳い曲なのである。抒情的でしかもスリリングさを兼ね備えた曲というのは、なかなかない。フェイド・アウトが口惜しい大名曲である。 一般にカンタベリーというと、 Soft Machine, Caravan を中心とする人脈が繰り広げる音楽体系ということになるが、たぶん、日本では、カンタベリーと一言言ったときに、 Hadfield and the North の音楽を思い浮かべる人はかなり多いのではないかと思われる。ある意味、カンタベリーの代名詞と言っても佳いかも知れない。なにしろメンバーが凄い。 Rober Wyatt 親衛隊が結成したこのバンドのメンツは、 Dave Stewart, Richard Sinclair, Phil Miller, Pip Pyle なのである。 Richard Sinclair は「ミスター・カンタベリー」の称号を戴いているほど。