Haskellという言語名は論理学者Haskell B. Curryの名前が由来。静的型付けコンパイラ型言語である。型推論機能が利用できるため関数や変数の型宣言を省略することがある程度は可能である。プログラムの記述は、「等式の左辺は右辺の式へと変換できる」という関係性の定義を列挙していく形になる。
Haskellにおける「関数」とは、数学における「関数」と同じく、引数の値が関数の返り値と一意に対応し不変であるものに限定される。関数が内部状態を持つことは許可されない。変数の初期値は再代入によって変更することはできない。このような制約を持つため、プログラムの各部分の実行順序を任意に選ぶことができ、今後並列演算への応用も期待できる。
Haskellにおいて、標準入出力を用いた処理など純粋な(数学における)関数表現では実現できない処理は「アクション」と呼ばれ「関数」とは区別しているが、両者を同一プログラム内で矛盾を起こさないように共存させる仕組みが提供されている。
変数・関数のスコープは、中括弧などコードブロックを囲むような記号は使わず、Pythonなどのようにソースコードのインデントの深さで表現される。
Haskellのためのフリーウェアの開発環境としてGHC(Glasgow Haskell Compiler)がデファクトスタンダード。他にはHugsなどがある。(これらはeclipseからも利用することもできる) Haskellで書かれた日本で有名なゲームにMonadiusというのがある。(GUIの処理ははOpenGLを利用したライブラリGLUTによるもの)
先述のように、Haskellは関数型言語の中でも、関数や再代入による状態変更を副作用として排除する道を選んだ純粋関数型言語である。
プログラミングスタイルとしては、その特性を活かして実行順序に関係なく、関係性の定義を宣言的に記述していくことで自動的に結果が導き出される宣言型プログラミングが推奨されている。従来のプログラミング言語とは全く違うその発想に、いわゆる命令型プログラミングに親しんできた人が接すると、拒絶反応を起こすか熱心な信奉者になるかに二極化するらしい。
「プログラミング言語が車だったら」のHaskellの項より選択抜粋
- 宇宙人の作った車と噂され、完全自動化を売りにしている。
- Haskellの運転席にはハンドルもペダルも無く、代わりに運転装置と一体化したカーナビが取り付けられている。ドライバーは実際の運転操作をする必要がなく、このカーナビに目的地の定義を入力すれば、目的地までの運転が自動的になされる。
- 従来の自動運転システムでは、道路状況を判断できず公道を走ることは無理であったが、Haskellはこれを奇想天外な方法で解決した。Haskellには目的地として「青信号のときのみ進行し、飛び出す子供に注意しながら、ブロック塀にかすることなく入った我が家の車庫」が指定できるのである。
- 結局Haskellを試乗した多くのドライバーは「目的地の定義を考えるより自分で運転した方が手っ取り早い」と不満をもらすそうな。
先述のように純粋関数型言語というのは副作用を排除する道を選んだ言語である。副作用とは、状態を変更することであるが、たとえば「画面に文字を表示する」という通常のプログラミング言語ではごく基本的なことに分類されることでも、Haskellでは画面の「状態の変更」であるとみなされる。
従って、純粋関数型言語であるためにはひたすら計算のみを行い結果の画面表示すら行わないという滑稽な状態にならざるをえない。もちろん、こんなことでは使い物にならないのでHaskellはモナドという仕組みを利用している。IOモナドというものがあり、副作用をIOモナドの処理に分離することにより、IOモナドの内部で純粋関数型言語であり続けるのである。
プログラミング言語を擬人化すると、「すべての家具が造り付けで固定されている部屋の中に引きこもる潔癖症の少女。外部とのやりとりは、壁にあいたIOモナドという小さな窓から行う」と表現されるかもしれない。
モナドはHaskellにとって副作用を起こすために必要なものではあったが、副作用を起こす以外にも様々な用途のモナドがあり、いわゆるぬるぽの対処をするMaybeモナドやListモナドなどがある。
main = putStrLn "Hello world!"
Haskellにおいて、文字列(String型)は文字(Char型)のリストとして扱う。リストは列挙した要素群を'['と']'で囲み、各要素の間は','で区切る。
文字列"Hello world!" は文字のリスト ['H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '!'] と同じもの。
Schemeにおけるリスト表現で、
(1 . (2 . (3 .(4 . ()))))
と
(1 2 3 4)
が同一のリストのを表わすように、
Haskellでは、
1 : (2 : (3 : (4 : []))) ※丸括弧を省略して、 1 : 2 : 3 : 4 : [] と書いてもよい
と
[1, 2, 3, 4]
は同一のリストを表わす。
(記号「→」以降に式の評価結果を記す)
| コードの説明 | Haskell | Scheme |
|---|---|---|
| リストの先頭要素取り出し |
head [1, 2, 3] |
(car '(1 2 3)) → 1 |
| リストの先頭要素を除いた残り部分の取り出し |
tail [1, 2, 3] |
(cdr '(1 2 3)) → '(2 3) |
| リストに先頭要素を追加 |
1 : [2, 3] |
(cons 1 '(2 3)) → '(1 2 3) |
| 空リスト判定 |
null [] |
(null? '()) → #t |
| 無名関数を使った演算 |
(\x y -> x + y) 1 2 |
((lambda (x y) (+ x y)) 1 2) → 3 |
繰り返し処理は、他言語によくあるforループのようなものではなく再帰呼び出しや高階関数を使った手法で書くのがHaskellなど関数型言語の基本的な流儀。
-- 同名の関数定義を複数用意して、引数によるパターンマッチングを利用する例 factorial :: Int -> Int -- 関数名はfactorialとし、引数は整数型の値を一つ取る、返り値も整数型の値 factorial 0 = 1 -- 引数が0の場合、返り値は1とする factorial n = n * factorial (n - 1) -- 引数が0以外の場合は再帰呼び出しを使って返り値を求める
-- 条件式if使った例
factorial :: Int -> Int
factorial n =
if n == 0 -- Haskell2010の規定により then/else節のインデントは揃えなくてもよい
then 1 -- 引数nが0の場合、返り値は1とする
else n * factorial (n - 1) -- それ以外の場合は再帰呼び出しを使って返り値を求める
-- ガード節を使った例
factorial :: Int -> Int
factorial n
| n == 0 = 1 -- 引数nが0の場合、返り値は1とする
| otherwise = n * factorial (n - 1) -- それ以外の場合は再帰呼び出しを使って返り値を求める
-- case構文のパターンマッチングを使った例 factorial :: Int -> Int factorial n = case n of
0 -> 1 -- 引数nが0の場合、返り値は1とする _ -> n * factorial (n - 1) -- それ以外の場合は再帰呼び出しを使って返り値を求める
同様の計算を再帰呼び出しを使わないで行うコードの例
-- 1からnまでの整数のリストを生成してその要素をすべてかけ算する例
-- 高階関数 foldl は、第2引数「1」と第3引数の整数リストの全要素を使って第1引数の「*」関数を適用 factorial :: Int -> Int factorial n = foldl (*) 1 [1..n] -- 具体的には、1 * (1 * 2 * 3 ....* n) の演算が行われる
下記の2冊は原書(英語)がwebで読めるらしい。
下記左上は上記左の翻訳書。
急上昇ワード改
最終更新:2025/12/11(木) 17:00
最終更新:2025/12/11(木) 17:00
ウォッチリストに追加しました!
すでにウォッチリストに
入っています。
追加に失敗しました。
ほめた!
ほめるを取消しました。
ほめるに失敗しました。
ほめるの取消しに失敗しました。