純粋関数型プログラミング言語Haskellを触ってみました。(『7つの言語 7つの世界』第7章)

7つの言語 7つの世界』(オーム社)の第7章で紹介されているHaskell(ハスケル)です。 
Haskell
http://www.haskell.org/haskellwiki/Haskell

1.Haskellについて

 Haskellは純粋関数型プログラミング言語です。「すごいH本」が出てWeb上で話題になったり、「関数型言語を学ぶならHaskellですよ!」と同僚に勧められたりと公私ともに非常に熱い言語のようです。
 他の関数型言語がオブジェクト指向言語の要素を取り入れたりするなど、関数言語としては非純粋ではないため、「純粋」なHaskellは関数型言語を学ぶのに向いているということなのでしょうか。
 

2.Haskellのインストール

 以下を参考にしました。Homebrewでもインストール可能なようですが、バージョンがやや古くなるそうなので、The Haskell Platformからインストーラー(.pkg)をダウンロードしてインストールしました。私はすでにインストールしていたので特に問題ありませんでしたが、事前にXcodeをインストールしておく必要があるそうです。
Haskell環境構築:Mac OS X – Google グループ
The Haskell Platform
 

3.Hello,World

ターミナルで「ghci」と打つとHaskellのシェルが起動します。

$ ghci
GHCi, version 7.4.1: http://www.haskell.org/ghc/  :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
Prelude>

終了は「:quit」。

Prelude> :quit
Leaving GHCi.

 
 
 ではではということで、数値や文字列を入力してみます。

Prelude> 1+1
2
Prelude> "Hello, World"
"Hello, World"
Prelude> "1"+"1"
<interactive>:4:4:
    No instance for (Num [Char])
      arising from a use of <code>+'
    Possible fix: add an instance declaration for (Num [Char])
    In the expression: "1" + "1"
    In an equation for</code>it': it = "1" + "1"
Prelude> "1" ++ "1"
"11"

  
 可変長なデータセットであるリスト

Prelude> [1,2]
[1,2]
Prelude> ["a","b"]
["a","b"]
Prelude> ['H','e','l','l','o',',','W','o','r','l','d']
"Hello,World"
Prelude> "Hello,World" ==  ['H','e','l','l','o',',','W','o','r','l','d']
True

 リスト内でシングルクォーテーションで囲うと一つの文字列として返されます。つまり、文字列も単なる文字のリストということらしいです。
 固定長なデータセットであるタプル

Prelude> (1,2,3)
(1,2,3)
Prelude> ("a","b","c")
("a","b","c")

 

4. 変数

 
 変数の定義。変数は変更可能らしい。

Prelude> let x = 1
Prelude> x
1
Prelude> let x = 2
Prelude> x
2

 

5. 外部ファイルとしてのプログラム作成とコンパイル、ロード

 
 モジュールdouble.hsのコンパイルとロード。

Prelude> :load double.hs
[1 of 1] Compiling Main             ( double.hs, interpreted )
Ok, modules loaded: Main.

 上でコンパイルしたファイルの実行。

*Main> double 5
10

 

6. 関数

6.1. 関数の定義

 add という関数を定義してみる。

Prelude> let add n= 1 + n
Prelude> add 2
3
 

 外部ファイルとして作成し、プログラムをコンパイルして上のプログラムを実行してみます。

add n = n + 1

 上を”add.hs”というファイル名で保存し、コンパイルして読み込む。

Prelude>:load add.hs
[1 of 1] Compiling Main             ( add.hs, interpreted )
Ok, modules loaded: Main.
*Main> add 1
2

 

6.2. 再帰

 階乗を求める。

Prelude> let fac x = if x == 0 then 1 else fac (x -1)*x
Prelude> fac 1
1
Prelude> fac 5
120
Prelude> fac 100
93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000

 

6.3. カリー化

 Haskellでは、「全ての関数は常に1つの引数をとる」のだそうです。つまり、1つの関数で2つ以上の引数をとることができないということです。「2つ以上の引数を使う関数を使いたい場合はどうするの?」という疑問が当然生じてきます。2つの引数をとる関数は引数を1つずつ順番に受け取りながら処理をしていくのだそうです。
 例えば、こんな感じで引数を2つとる関数testを定義しています。

Prelude> let test  a b = a + b * 2
Prelude> test 2 4
10

 一見、上のtest関数は2つの引数をとるように見えますが、実はこの場合、
(test 2) 4
 と実行していることになるのだそうです。つまり、まず最初に引数2をとり、
test 2 
 を実行しますので、引数2が a に代入されて
2 + b2
 となり、続けてbに引数4が代入されて
2 + 4
2
となり、10という結果が返ってきます。
 だらだらと書いてしまいたが、引数を一つずつ処理していくプロセスがカリー化と呼ばれるのだとか。
 
 

7.型クラス(type class)

 Haskellは型推論を行ってくれるので、特に型を定義をする必要はありません。しかし、プログラマがきちんと型付けしたい場合もあります。そんなときは

Prelude> 1 :: Int
1

“:t”で上で定義した「型」を確認することもでき・・・・・

Prelude> :t 1
1 :: Num a => a

 Intと型を定義したのにNumが返ってきます。以下によるとNumは型クラス(type class)で、Intは(インスタンス)なのだそうです。“:t”では上で「型」と書いてしまいましたが、型クラスを返すみたいですね。
10分で学ぶHaskell – HaskellWiki
オブジェクト指向から理解する型クラス – think and error
 
 次は文字列です。
 
 文字列の場合は、StringというCharというがあるようです。ちなみに以下によるとChar型とString型はPreludeモジュールで定義されたRead、Show、Eq、 Ord、Enum および Boundedという型クラスの型(インスタンス)のようです。
The Haskell 98 Report: 定義ずみの型とクラス
Prelude Standard types, classes and related functions
  Charは文字、Stringは文字列を示す型なのだそうで、ダブルクォーテーションで囲ってしまうと文字列になってしまうらしく、Charという型では定義できません。シングルクォーテーションで囲った1文字の文字であればCharで定義できます。

Prelude> "a" :: String
"a"
Prelude> :t "a"
"a" :: [Char]
Prelude> 'b'::Char
'b'
Prelude> :t 'b'
'b' :: Char
Prelude> "b"::Char
<interactive>:21:1:
    Couldn't match expected type <code>Char' with actual type</code>[Char]'
    In the expression: "b" :: Char
    In an equation for `it': it = "b" :: Char
Prelude> 'abc'::Char
<interactive>:20:1:
    Syntax error on 'abc'
    Perhaps you intended to use -XTemplateHaskell

 しかし、文字列”a”は「3.Hello,World」で少し触れたように[‘a’]というリストでもあるわけですので、リストの要素として扱えばCharになるんでしょうか、たぶん。
  Haskellが組み込みの型クラスと型はPreludeモジュールで定義されているようです。その型クラスと型は上で一度紹介しましたが、以下で一覧されています。
Prelude Standard types, classes and related functions
 Slideshareでは型クラスと型についてわかりやすく紹介している発表資料がいくつもありましたので、そのうち2つを掲載します(一つは上で紹介したものですが)。