この文章は About Haskell(A Short Introduction to Haskell)を
永田章人が勝手に翻訳したものです。著作権などに問題がある場合は即刻撤去しますので、永田に御連絡下さい。
ちなみに文章がおかしいところは英語に自信がないところです。
表現が間違っていたり、誤植など直した方がよいところがあれば是非御指導ください。
純粋な関数型言語であるHaskellは以下を提供します。
ソフトウェアの生産では、多くの時間が費やされるのは仕様の決定、デザイン、 メンテナンスであり、プログラミング自体ではありません。 関数型言語は仕様を書くのに最適です。その仕様は同時に(テストとデバッグもできる)実行可能なプログラムでもあり、 最終成果物としてのプログラムの最初のプロトタイプとすることができます。
同様に、関数型プログラムではコードが短く簡潔になり、 厳格な副作用の制御により予期のできない相互作用を排除することが可能なため、 メンテナンスがしやすくなります。
この表計算ソフトにおける計算の順序が指定されないということから、 代入という概念はそれほど役にたたないという面白いことが言えます。 とりわけ、もし代入がいつ起こるか正確にわからないのであれば、それを有効利用することはできません! このことは、 基本的に代入文を慎重に並べていくことにより計算を構成していく C言語や、メソッドの呼び出し順が重要となるJavaといったような慣習的な言語とはかなり対称的です。
この レベルの低い"どう" ではなく レベルの高い"何" に焦点を当てているのが、 関数型言語が他と最も異なる特徴と言えます。
他のよくしられた関数型言語に近い言語として、 標準的なデータベースクエリー言語である SQLがあります。 SQLのクエリーは投射、選択、結合などを含めた式になります。 クエリーには どの関係が計算されるかが記述され、どのように計算するかは 記述されません。 もちろんクエリーはどのような順序で評価されても構いません。 SQLの実装の多くは、(考え得る中で)最も最適な式の評価順序を見つけることで クエリーの最適化を行ないます。
qsort [] = [] qsort (x:xs) = qsort elts_lt_x ++ [x] ++ qsort elts_greq_x where elts_lt_x = [y | y <- xs, y < x] elts_greq_x = [y | y <- xs, y >= x]
qsort( a, lo, hi ) int a[], hi, lo; { int h, l, p, t; if (lo < hi) { l = lo; h = hi; p = a[hi]; do { while ((l < h) && (a[l] <= p)) l = l+1; while ((h > l) && (a[h] >= p)) h = h-1; if (l < h) { t = a[l]; a[l] = a[h]; a[h] = t; } } while (l < h); t = a[l]; a[l] = a[hi]; a[hi] = t; qsort( a, lo, l-1 ); qsort( a, l+1, hi ); } }それではHaskellや関数型プログラミングの何がいいのか考えてみましょう。 関数型プログラミングのより詳細な事例については、
Why Functional Programming Matters by John Hughes, The Computer Journal, Vol. 32, No. 2, 1989, pp. 98 - 107. Also in: David A. Turner (ed.): Research Topics in Functional Programming, Addison-Wesley, 1990, pp. 17 - 42. (山下伸夫さんによる邦訳 : なぜ関数プログラミングは重要か)で見つけることができます。上の論文に影響されたあまりフォーマルでないエッセイとして
Why Does Haskell Matter by Sebastian Sylvanがあります。
qsort [] = [] qsort (x:xs) = qsort elts_lt_x ++ [x] ++ qsort elts_greq_x where elts_lt_x = [y | y <- xs, y < x] elts_greq_x = [y | y <- xs, y >= x]最初の行はこう読むことができます: "空のリスト([]と書かれている)をソートした結果は空のリスト"。 同様に2行目はこう読むことができます: " 最初の要素がxで、 残りの要素がxsであるようなリストを ソートするためには、 xsのうちxより小さい全ての要素 (elts_lt_xとする)と、 xsのうちxと等しいかxより大きい全ての要素 (elts_greq_xとする)と をそれぞれソートし、それぞれの結果にxを 真ん中に挟み込み結合(++)しろ。 elts_lt_xの定義はすぐ下で与えられています : リストxsから引き出された要素yのうち、 xより小さい全てのyのリスト"。 elts_greq_xの定義も同様です。 この構文は、"|"を "such that"、 "<-"を"取り出す(drawn from)"と読むことで、 標準的な数学の集合の表記を 連想できるようになっています。 空ではないリストをソートするように要求されたとき、qsortはelts_lt_xと elts_greq_xをソートするために自分自信を呼び出します。 これらのリストは元のqsortに与えられたリストよりも小さいのでOKです。 そのようにして、この分けてソートするというプロセスにより、対象となるリストは空のリストまで短くなります。 空のリストのソートはqsortの最初の行で与えられた、 ほぼ自明の方法でソートされます。
多相型は再利用性を高めるのです。
grep printf Foo.c | wcは Foo.c というファイルの中のprintfという文字列を含む行の数を数えます。 "grep printf Foo.c"というコマンドが"printf"を含む全ての行を抜き出してくる一方で、 "wc"コマンドがそれらの数を数えます。 "|"と書かれているのはパイプで、 最初のコマンドの出力を受けとり、2つ目のプログラムへ渡す役目をします。 2つのコマンドは同時に実行され、最初のコマンドの 出力は幾分の遅れはあるにしても、その直後に使用されることになります。 この方法では大きな中間ファイルを生成する必要はありません。 wc はgrepから出力される行に"依存している" と考えることができます。
もし、2つ目のコマンドが最初のコマンドの出力のうちの一部しか使用しないのであれば、最初のコマンドは 最後まで次以降する必要はないかもしれません。例えば
grep printf Foo.c | head 5は "printf"が含まれている行のうちの最初の5行だけを表示します。 grepを実行が中断されるかもしれないということを考慮にいれた プログラムに書き直す必要はありません。
非厳格な言語はこのような種類の要求駆動型の評価方法を提供します。 データ構造は答を導くのに十分となる分だけ評価され、 結局評価されないような部分式も含んでいるかもしれません。 Unixのコマンドの例でわかるように、 このことは既存のプログラムを組み合わせるための強力な"糊"を提供しています。 このことは命令型で書く場合よりも、 プログラムを再利用性の高い小さいなプログラムに分割できることを表しています。 遅延評価により、よりモジュール性の高いプログラムを書くことが可能となります。
関数型言語における強力な抽象化のメカニズムに高階関数があります。 Haskellでは関数はファーストクラスです。 : 関数は他の関数へ自由に渡すことが出来るし、 関数の返り値として返すこともでき、データ構造の中に格納したりできます。 高階関数を適切に使うことにより多くのプログラムの構造やモジュール性を向上させることができます。
全ての関数型言語はプログラマをこの重荷から解放させてくれます。メモリ確保と初期化は暗黙のうちに行なわれ、確保された領域はガーベジコレクターにより自動的に回収されます。 この記憶領域の確保とガーベジコレクションの技術は現在では適度に成熟しており、 小さいコストで実行できます。
要するに、Cのクイックソートは読みやすさと引き換えに、 記憶領域のコストを減らすためのかなり巧妙な記憶領域管理を行っています。
パフォーマンスが最も重視されるようなアプリケーションや、 低レベルのアルゴリズムにより細部をチューニングすることが目的の場合は、 計算がどう実行されるかをより綿密に制御するコードを提供できるため、 Cのような命令型言語の方がHaskellを選ぶよりも賢明でしょう。
同様に、仮想メモリページングシステムは喜んで受け入れられています。無限の仮想アドレススペースなどのよりサポートの高いプログラミングモデルを得ることができています。 今日、明示的なメモリの重ね付けは終わりました。
関数型言語は高いレベルのプログラミングモデルへ向けて大きなステップを踏もうとしています。 プログラムはよりデザイン、記述、保守がしやすくなりますが、 マシンの詳細な制御はできなくなります。 このことはほとんどのプログラムにとって許容範囲内です。
表現力のある構文、従来の整数や浮動小数点数、 真偽値型に加え任意精度の整数や有理数 豊富な組み込みデータ型を備えており、 数値計算から抽象的なアプリケーションまで幅広くサポートできることに、 重点をおいてデザインされています。
多くの 実装が利用可能で、全てフリーとなっています。 初心者の方には 軽量で可搬性の高い Haskellインタプリタである Hugs をお勧めします。
確かに関数型プログラミングは見方を変えることを要求しており、 それを難しいと感じるプログラマもいます。 しかし、ErlangのEricssonによるプログラマのトレーニングの実験では、 プログラマが 一朝一夕でできると考えずに、真剣にトレーニングを行なえば、 多くの場合は移行は容易いとしています。関数型プログラムは遅いのではないですか?
かつてはそうでしたが、現在ではコンパイラが追いついて来ました。 パフォーマンス大きく依存するアプリケーションを除けば、 全てのHaskellプログラムは十分な速さで実行することができます。CまたはC++で書かれた大規模アプリケーションが既にあるのですが、 システムの全体を書き直さずに関数型言語の恩恵を受けることができますか?
Haskellは 既存のアプリケーションにいろいろな方法で巧みに統合されて来ました。 HaskellDirectは IDL (Interface Description Language)ベースのツールで、 これによりHaskellプログラムを他のソフトウェアコンポネントと同時に使うことができます。 C/C++への低レベルインタフェースはGreen Cardで作成することができます。 これにより HaskellとCの強力な統合が可能です。 これらのツールを使って多くの多言語から構築された多くのシステムが、成功を納めています。Haskellがサポートしているライブラリにはどういうものがありますか?
多くのHaskell用ソフトウェアライブラリが開発されました。 list of Haskell libraries に利用可能なライブラリの全リストがあります。その他のHaskell用のソフトウェアツールにはどんなものがありますか?
Glasgow Haskellではプロファイラが利用できます。 これによりあなたのプログラムの中のどの部分が、 実行時間のメモリスペースのほとんどの割合を消費しているかがわかります。 Chalmers Haskellはスペースプロファイリングツールと、 プログラムを並列に動かした場合の実験ができるquasi-paralellシミュレータが利用できます。 Hugsでも同様のツールが利用可能です。サポートや支援を受けることは可能ですか?
残念ながら、とりあえず現時点ではできません。 Haskell の実装はどれも研究グループにより作られたものであり、 これらの実装と企業が契約を結ぶことで、 市場でのチャンスが生まれるのではないか思います。 私たちはこのようなことを喜んでする人と共に働きたいと切に願っています!どうしたらHaskellが学べますか?
より多くの例や説明は Gentle Introduction to Haskell (山下伸夫さんによる邦訳 : やさしいHaskell入門)を参照してください。 Haskellを利用するための書籍も数多く存在します。 bookshelfを参照してください。
この文章はSimon Peyton Jonesの論文に基づいています。(それをさらに日本語訳したものです。)
永田のメモ: 非厳格(Non-strict)というのは関数の引数が遅延評価されることを指すようです。