この文章は About Haskell(A Short Introduction to Haskell)を 永田章人が勝手に翻訳したものです。著作権などに問題がある場合は即刻撤去しますので、永田に御連絡下さい。

ちなみに文章がおかしいところは英語に自信がないところです。 表現が間違っていたり、誤植など直した方がよいところがあれば是非御指導ください。

About Haskell

なぜHaskellをつかうのか?

純粋な関数型言語であるHaskellは以下を提供します。

実質的なプログラマの生産性の向上(Ericssonによる 電話のソフトウェアを用いた一連の実験によれば、9から25倍の向上が 見られました)。

短く、簡潔で、より保守性の高いコード。

エラーの少ない、高い信頼性。

プログラマとの"意味のギャップ"が小さい言語。

納期短縮

ソフトウェアの生産では、多くの時間が費やされるのは仕様の決定、デザイン、 メンテナンスであり、プログラミング自体ではありません。 関数型言語は仕様を書くのに最適です。その仕様は同時に(テストとデバッグもできる)実行可能なプログラムでもあり、 最終成果物としてのプログラムの最初のプロトタイプとすることができます。

同様に、関数型プログラムではコードが短く簡潔になり、 厳格な副作用の制御により予期のできない相互作用を排除することが可能なため、 メンテナンスがしやすくなります。

関数型言語とは?

セルが計算される順序は指定されません。- その代わりに 表計算ソフトがそれぞれのセルの依存性に応じてセルの計算をしてくれるであろうという考えが根底にあります。

表計算ソフトがメモリをどのように配置するかを指定することはありません。 - むしろ、 表計算ソフトが表面的には無限のセルの列があるように見せてくれて、 実際に使用される時に初めてメモリを配置してくれることを期待します。

ほとんどの場合、各セルの値は、その値を計算するためのコマンドの列 ではなく、ただ一つの式により指定されます。 (式の中の部分式の評価順は問われません)。

この表計算ソフトにおける計算の順序が指定されないということから、 代入という概念はそれほど役にたたないという面白いことが言えます。 とりわけ、もし代入がいつ起こるか正確にわからないのであれば、それを有効利用することはできません! このことは、 基本的に代入文を慎重に並べていくことにより計算を構成していく C言語や、メソッドの呼び出し順が重要となるJavaといったような慣習的な言語とはかなり対称的です。

この レベルの低い"どう" ではなく レベルの高い"何" に焦点を当てているのが、 関数型言語が他と最も異なる特徴と言えます。

他のよくしられた関数型言語に近い言語として、 標準的なデータベースクエリー言語である SQLがあります。 SQLのクエリーは投射、選択、結合などを含めた式になります。 クエリーには どの関係が計算されるかが記述され、どのように計算するかは 記述されません。 もちろんクエリーはどのような順序で評価されても構いません。 SQLの実装の多くは、(考え得る中で)最も最適な式の評価順序を見つけることで クエリーの最適化を行ないます。

関数型言語の何がいいのか?

Haskellで書かれたクイックソート

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]

Cで書かれたクイックソート

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 ); } }

1. 簡潔さ

2. 理解のしやすさ

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 の最初の行で与えられた、 ほぼ自明の方法でソートされます。

3. コアダンプがない

4. コードの再利用

多相型は再利用性を高めるのです。

5. 強い糊

grep printf Foo.c | wc

Foo.c

grep printf Foo.c

printf

wc

|

wc

grep

もし、2つ目のコマンドが最初のコマンドの出力のうちの一部しか使用しないのであれば、最初のコマンドは 最後まで次以降する必要はないかもしれません。例えば

grep printf Foo.c | head 5

printf

grep

非厳格な言語はこのような種類の要求駆動型の評価方法を提供します。 データ構造は答を導くのに十分となる分だけ評価され、 結局評価されないような部分式も含んでいるかもしれません。 Unixのコマンドの例でわかるように、 このことは既存のプログラムを組み合わせるための強力な"糊"を提供しています。 このことは命令型で書く場合よりも、 プログラムを再利用性の高い小さいなプログラムに分割できることを表しています。 遅延評価により、よりモジュール性の高いプログラムを書くことが可能となります。

6. 強力な抽象化

関数型言語における強力な抽象化のメカニズムに高階関数があります。 Haskellでは関数はファーストクラスです。 : 関数は他の関数へ自由に渡すことが出来るし、 関数の返り値として返すこともでき、データ構造の中に格納したりできます。 高階関数を適切に使うことにより多くのプログラムの構造やモジュール性を向上させることができます。

7. 組み込みのメモリ管理

malloc

malloc

malloc

全ての関数型言語はプログラマをこの重荷から解放させてくれます。メモリ確保と初期化は暗黙のうちに行なわれ、確保された領域はガーベジコレクターにより自動的に回収されます。 この記憶領域の確保とガーベジコレクションの技術は現在では適度に成熟しており、 小さいコストで実行できます。

Cの方がよい場合

要するに、Cのクイックソートは読みやすさと引き換えに、 記憶領域のコストを減らすためのかなり巧妙な記憶領域管理を行っています。

パフォーマンスが最も重視されるようなアプリケーションや、 低レベルのアルゴリズムにより細部をチューニングすることが目的の場合は、 計算がどう実行されるかをより綿密に制御するコードを提供できるため、 Cのような命令型言語の方がHaskellを選ぶよりも賢明でしょう。

関数型 vs 命令型

同様に、仮想メモリページングシステムは喜んで受け入れられています。無限の仮想アドレススペースなどのよりサポートの高いプログラミングモデルを得ることができています。 今日、明示的なメモリの重ね付けは終わりました。

関数型言語は高いレベルのプログラミングモデルへ向けて大きなステップを踏もうとしています。 プログラムはよりデザイン、記述、保守がしやすくなりますが、 マシンの詳細な制御はできなくなります。 このことはほとんどのプログラムにとって許容範囲内です。

Haskellとは何か?

表現力のある構文、従来の整数や浮動小数点数、 真偽値型に加え任意精度の整数や有理数 豊富な組み込みデータ型を備えており、 数値計算から抽象的なアプリケーションまで幅広くサポートできることに、 重点をおいてデザインされています。

多くの 実装が利用可能で、全てフリーとなっています。 初心者の方には 軽量で可搬性の高い Haskellインタプリタである Hugs をお勧めします。

誰か関数型プログラミングを使っている人はいますか?

ドイツの有名なソフトウェア会社であるSoftware AGは、 関数型言語でプログラムされたエキスパートシステム(ナチュラルエキスパート)を商品化しています。 彼らは、背後にあるデータベースシステムにアクセスするアプリケーションを開発するのに、 こういった言語を利用すると容易になると考えています。 これらは全てIBMのメインフレーム上で動作しています。

Ericssonは未来の電話のアプリケーションに向け、 Erlangという新しい関数型言語を開発しました。 彼らは既に130,000行に及ぶErlangのアプリケーションを書いており、 短く記述でき開発が早いと言っています。

Amoco は、 彼らの所有するメインのオイル貯蔵池のシミュレーションを行なう重要なアプリケーションの 大量のコードを、関数型言語で書き直すという実験を行ないました。 出来上がったプログラムは非常に小さく、 書き直したことで既存のアプリケーションには多くエラーがあったことがわかりました。 その後、Amocoは関数型プログラムを書き直しよい結果を残しました。

MITRE社のリサーチャーはデジタルシグナルプロセッシングアプリケーションのプロトタイプ作成にHaskellを使っています。

Durham大学のリサーチャーは自然言語理解のための30,000行のプログラムLOLITAを作成する７年越しのプロジェクトに関数型言語を使用しました。

QueryとはO2というオブジェクト指向データベースシステムのクエリー言語です。O2Queryはおそらくほとんどの入り組んだ商用利用が可能なオブジェクト指向データベースクエリー言語であり、それは関数型言語です。

ICAD Inc は機械技師や航空技師向けのCADシステムを商品としています。 技師達が自分のデザインを記述する言語は関数型言語で、 そこでは、デザイン中の現在スクリーンに写し出されていない部分が再計算されるのを 避けるために遅延評価が用いられています。 これによりパフォーマンスが大幅に向上します。

排他的な例 : Glasgow Haskell コンパイラ(ghc) は Haskellで 30,000行程度で 記述されています。

その他のよくある質問

確かに関数型プログラミングは見方を変えることを要求しており、 それを難しいと感じるプログラマもいます。 しかし、ErlangのEricssonによるプログラマのトレーニングの実験では、 プログラマが 一朝一夕でできると考えずに、真剣にトレーニングを行なえば、 多くの場合は移行は容易いとしています。

かつてはそうでしたが、現在ではコンパイラが追いついて来ました。 パフォーマンス大きく依存するアプリケーションを除けば、 全てのHaskellプログラムは十分な速さで実行することができます。

Haskellは 既存のアプリケーションにいろいろな方法で巧みに統合されて来ました。 HaskellDirectは IDL (Interface Description Language)ベースのツールで、 これによりHaskellプログラムを他のソフトウェアコンポネントと同時に使うことができます。 C/C++への低レベルインタフェースはGreen Cardで作成することができます。 これにより HaskellとCの強力な統合が可能です。 これらのツールを使って多くの多言語から構築された多くのシステムが、成功を納めています。

Glasgow Haskellではプロファイラが利用できます。 これによりあなたのプログラムの中のどの部分が、 実行時間のメモリスペースのほとんどの割合を消費しているかがわかります。 Chalmers Haskellはスペースプロファイリングツールと、 プログラムを並列に動かした場合の実験ができるquasi-paralellシミュレータが利用できます。 Hugsでも同様のツールが利用可能です。

残念ながら、とりあえず現時点ではできません。 Haskell の実装はどれも研究グループにより作られたものであり、 これらの実装と企業が契約を結ぶことで、 市場でのチャンスが生まれるのではないか思います。 私たちはこのようなことを喜んでする人と共に働きたいと切に願っています!

この文章はSimon Peyton Jonesの論文に基づいています。(それをさらに日本語訳したものです。)

最終更新日: 12 December 2001(日本語版は2003年9月24日)

永田のメモ: 非厳格(Non-strict)というのは関数の引数が遅延評価されることを指すようです。