Haskell について

1. Haskell とは

Haskell(ハスケル)は,1990 年に発表された純粋関数型言語です. 当時乱立していた(特に遅延評価型の)関数型言語を統一しようというモチベーションから,複数の計算機科学者の手によって生み出されました. ちなみに,Haskell という名前は,アメリカの数学者 Haskell Curry(1900-1982)の名に由来しています.

Haskell の構文や機能は,Haskell の登場以前に存在した Miranda という遅延評価型の純粋関数型言語に強く影響を受けています.

2. Haskell の特徴

Haskell は次のようなキーワードで表される特徴をもつ言語です.

  • 純粋関数型言語

  • モナド

  • 強力な型システム

  • 型クラス

  • 遅延評価

これらのキーワードをもとに,Haskell の特徴を眺めていきましょう.

2.1. 純粋関数型言語

関数型言語(functional language)とは,"関数" によってプログラムを構成するプログラミングスタイルを基本とする言語のことです. Haskell のほかに,LISP や ML が伝統的な関数型言語として有名です.

ここでいう 関数(function)とは,いわゆる数学の関数と同じく,同じ引数に対して同じ値を返すプログラム部品 のことをいいます. 例えば,$f(1)$ という式の値がある時点で 5 と評価され,別のある時点(例えば 1 秒後)に 7 と評価されるなら,$f$ はここでいう関数ではありません.

関数と関数もどき

その中でも特に 純粋関数型言語(purely functional language)と呼ばれる言語とは,同じ式はどのタイミングで評価しても同じ値になる という特徴をもつ関数型言語のことをいいます. 純粋関数型言語では,例えば $f(x)$ という式の値があるタイミングで 5 だったら,他のタイミングで $f(x)$ を評価しても必ず 5 になることが保証されます.

Haskell は純粋関数型言語ですが,LISP や ML は純粋関数型言語ではありません. LISP や ML では,実用性などの観点から,純粋性を破るようなプログラムを書くことが許されています.

"同じ式はどのタイミングで評価しても同じ値になる" という性質を,参照等価性(referential transparency)といいます. この性質は,プログラムの解析を容易にし,品質のよいプログラムを構築するのに役立ちます.

変数に格納したり,関数の引数や返り値に使ったりできるデータのことを,第一級オブジェクト(first-class object)と呼びます. 関数がプログラムの主役となる関数型言語では,関数は第一級オブジェクトです. つまり,関数リテラルが存在し,関数を別の関数の引数や返り値に使ったりすることが簡単にできます.

例えば,Haskell なら,リスト [1, 2, 3, 4, 5] の要素を 2 乗する処理を,関数式を使って次のように簡単に書くことができます(-- から右側はコメントです).

map (\x -> x * x) [1, 2, 3, 4, 5]  -- 値: [1, 4, 9, 16, 25]

2.2. モナド

純粋関数型言語は理想的な性質をもった言語ですが,現実のプログラムは(特に入出力周りで)副作用とは切っても切れない関係にあり,そこでどう純粋性を担保するかが課題となります. Haskell では,純粋性を保ちながら副作用とうまく付き合うために,数学の圏論(category theory)に由来するモナド(monad)という概念を採用しています.

Haskell ではモナドのための強力な構文糖が提供されており,これがモナドの存在を Haskell においてより特別にしています. 次のプログラムに示す do 記法と呼ばれる構文糖がそれです.

main = do
    putStr "あなたの名前: "
    name <- getLine
    let greeting = "こんにちは," ++ name ++ "さん."
    putStrLn greeting

純粋関数型言語でありながら,いかにも手続き型言語であるかのような馴染みのあるスタイルでプログラムを書くことができます.

純粋関数型言語で副作用を実現する方法には,モナド以外にもいろいろな手法が知られています. 実は,初期の Haskell では,モナドではなく "遅延リスト" という仕組みで副作用を実現していました. また,Haskell よりも古い純粋関数型言語である Clean では,"一意型" という概念を採用して純粋性を担保しています.

2.3. 強力な型システム

Haskell は強い静的型付けの型システムを採用しています. また,強力な型推論機構を持っています.

append x y = x ++ y

このプログラムは,2つのリストを連結する関数 append を定義した Haskell プログラムです. コード上にはどこにも型に関する情報を書いていませんが,コンパイラは勝手に型推論を行い,append の型を次のように推論してくれます.

append :: [a] -> [a] -> [a]
この強力な型推論機構は ML に由来するもので,これを実現するアルゴリズムは Hindley-Milner 型推論アルゴリズム として知られています.

2.4. 型クラス

Haskell の型システムは,型クラス(type class)と呼ばれる概念を採用している点で特徴的です. 型クラスは,データ型をカテゴライズする役割を持つ概念です.

例えば,数値型全般を表す Num 型クラスは,Haskell における代表的な型クラスのひとつです. Num 型クラスには IntDouble など,数値を表すデータ型が属します.

Numクラス

2.5. 遅延評価

Haskell は 遅延評価(lazy evaluation)と呼ばれる式の評価方式を採用しています. これは,次のような評価方式のことをいいます.

  • 式の評価はできるだけ先延ばしにする.

  • 評価する必要のない式は評価しない.

遅延評価は,計算の効率化に役立つほか,無限長のデータの取り扱いにも役立ちます.

take 5 [1..]  -- 値: [1, 2, 3, 4, 5]

これは,無限長のリストに対して関数を適用し,リストの先頭 5 要素を取り出す Haskell プログラムです. もし遅延評価でなかったら,このプログラムは引数の無限リストを評価しようとして暴走してしまうでしょう.

3. Haskell の仕様

1990 年に Haskell 1.0 が発表されて以来,言語仕様は何回か改訂を重ねてきました. 直近では 2010 年に Haskell 2010 言語仕様が定められています.

言語仕様

1990

Haskell 1.0

1991

Haskell 1.1

1992

Haskell 1.2

1996

Haskell 1.3

1997

Haskell 1.4

1998

Haskell 98

2002

Haskell 98 Revised Report

2009

Haskell 2010

すべての言語仕様は,次のページから参照できます.

また,山下さんが管理されている次のサイトで,いくつかの仕様の邦訳が見れます.

現在は,Haskell Prime 2020 委員会が時期バージョンの策定に向けて動き始めているようです.

4. Haskell の実装

Haskell にはさまざまな実装が存在しますが[1], 現在は Grasgow Haskell Compiler(GHC) がデファクトスタンダードとしての地位を占めています. このチュートリアルでも,開発環境として基本的には GHC を想定しています.

ビルドツール(パッケージ管理ツール)としては,CabalStack が使われます. 後発の Stack は,Cabal で問題となっていた,パッケージの依存関係地獄を解決するために生まれた存在です. 徐々にリプレースが進んでいます.

そして,上記の GHC,Stack,Cabal をまとめてインストールできる,Haskell Platform というディストリビューションがあります. Haskell の開発環境を構築する際には,Haskell Platform をインストールするとよいでしょう.

5. 関連サイト

Haskell でプログラムを開発する上で役立つサイトを紹介します.


1. Haskell の実装に関する情報はここにまとめられています: Implementations - Haskell Wiki