Haskellプログラミングの基礎【現役エンジニアがわかりやすく解説】

PROGRAM

Haskellプログラミングの基礎を知りたい方に。

この記事を読まれている方は、

  • Haskellのプログラミングがどんなものか知りたい
  • Haskell言語の簡単な書き方が知りたい
  • Haskellが使われる場面を知りたい

おそらくこのように考えている方が多いと思います。

この記事を書いている私は、
プログラミング未経験から転職を果たした現役のWeb系エンジニアです。
エンジニア歴は3年ですが、本業とは別にフリーランスの副業で月10万円ほど、多いときで20万円ほどの収入があります。

この記事では、現役のエンジニアが、Haskellプログラミングの基礎を解説しています。

プログラミング言語のHaskellの特徴

プログラミング言語のHaskellの特徴

Haskellはプログラミング言語のひとつで、静的型付けの言語で、純粋関数型の言語です。
※ただし、型推論があります。
コンパイル言語としても動きますし、スクリプト言語としても動かすことができます。

静的型付けの言語とは、簡単に言うと、データを文字列型や数値型、論理型などに分ける必要がある言語のことです。しかし、Haskellでは型推論がありますので、明確に型を指定しなくてもコンパイルが通ります。もちろん、静的型付けなので、コンパイル後には型が決定しています。

コンパイル言語とは、プログラムの実行前に、開発者の方でコンパイラーを使って構文チェックなどをしたうえで中間言語に変換しないと動作しない言語のことです。

一方、コンパイル言語と対をなすスクリプト言語とは、プログラム実行前にコンパイルをする必要のない言語のことで、書いた後はすぐにプログラムを実行することができる言語のことです。

一般に、コンパイル言語は、処理速度がスクリプト言語に比べて速くなります。速度が求められる処理では、コンパイル言語が使用されることが多いです。

関数型言語とは、関数の組み合わせでプログラミングを行う言語のことです。具体的には、変数の再代入などができず、関数を再帰的に呼ぶなどしてループを実現したりします。
ただ、手続き型言語のように書くこともよくありますので、手続き型言語と関数型言語とのハイブリッドとも言えます。

Haskellを使えば、関数型言語のため、可読性が高いプログラムを書けるようになります。
具体的には、関数のため、引数や返り値の型をきちんと決めますし、さらに変数の再代入もないので、アンチパターンを実装することがあまりないということです。

ただし、その分学習コストもかなり高いように感じられます。
手続き型言語やオブジェクト指向型言語に慣れている方たちにとっては、関数型の学習はとても苦痛に感じることでしょう。

例えば、for文やwhile文などもありません。
変数の再代入ができないので、i++(カウントアップ)ができないためです。

また、遅延評価によって、必要とされるまで変数を評価しないため、計算量を極力抑えることができますので、上手くコーディングすればパフォーマンスが出ます。

統合開発環境(IDE)では、ghc-modなどがあります。もちろんフレームワークもありますので、アプリケーションの開発は十分行えます。ただし、サンプルは他の言語に比べるとかなり少ないので、業界では関数型言語はどうしても敬遠されてしまう印象があります。

Haskellの用途

Haskellの用途

Haskellは静的型付けで関数型言語のため、アンチパターンを実装しにくく、可読性の高いコーディングを行うことができます。

例えば、FacebookやIBM、ツイッターなどのアプリケーションで利用されています。Facebookではスパム対策のプログラムにHaskellが使われています。

ただ、関数型言語はこの業界ではあまり主流とは言えず、エンジニアの数で言えば、オブジェクト指向型や手続き型言語の方が圧倒的に多いので、Haskellの案件数は相対的に少ないです。

コストパフォーマンスを考えれば、その他の言語を学ばれる方が良いです。

Haskellの書き方

Haskellの書き方

ここでは簡単に書き方を少しだけ紹介します。実際に始める気になったら、ぜひ参考書などの教材を参考にして、試してみて下さい。

Hello World!を画面に出力する

まずはお決まりのHello World!という文字列を画面に出力する方法から。

main = putStr "Hello World!"

これで出力することができます。
一部の手続き型言語と同じように、main関数を書かなければ動きません。
コンパイルはしてもしなくても動かすことが可能です。

変数宣言と分岐処理

次は少し踏み込んで、変数宣言と分岐処理をしてみます。

main = do
    if age < 20 then
        putStr "未成年"
    else
        putStr "成人"
    where
        age = 19 

上記の例では、まずageという数値型の変数を作り、その中に19という数値を入れています。
次にageが20未満であれば”未成年”という文字列を、それ以外であれば”成人”という文字列を出力するような分岐処理をしています。

配列と繰返処理

続いて、配列の使い方と繰返処理について、解説します。

ここでは配列を作って、以下のようにデータを出力することにしましょう。

佐藤,田中,小林

これを出力するコードは以下です。

printStudents i
    | i == length students = do
        return ()
    | otherwise = do
        if i < length students -1
            then putStr (students !! i ++ ",")
            else putStr (students !! i)
        return =<< printStudents (i + 1)
    where
        students = ["佐藤","田中","小林"]

main = printStudents 0

上記の例では、関数を使って手続き型言語のfor文のようにプログラムを書いています。通常Haskellではインデックスを指定して要素を取り出すということはあまり行いませんが、そちらは誰でも簡単に書けてしまうので、あえて別の書き方をしてみています。

まず文字列型のリストstudentsに「佐藤」「田中」「小林」の3つの文字列を格納しています。
その後、数値型のカウンタ変数iを0に初期セットし、リストstudentsの要素の数に到達するまでループで再帰的に同じ関数を呼び続け、その引数としてiを1ずつ増やしてから設定し、まわしていきます。その際に、putStr関数を使い、リストstudentsのi番目の要素の値を出力し、最後の要素以外、その後に「,(カンマ)」を付けて出力しています。

構造体の取り扱い

最後に構造体の取り扱い方法について解説します。

ここではエクセルのようなデータを作って、以下のようにデータを出力することにしましょう。

名前:佐藤,性別:男性
名前:田中,性別:女性
名前:小林,性別:女性

これを出力するコードは以下です。

import qualified Data.Map.Strict as Map
import Data.Map (Map())
import Data.Maybe

printExcelData i
    | i == length excel_data = do
        return ()
    | otherwise = do
        putStr (Data.Maybe.fromJust(Map.lookup "A" (excel_data !! i)))
        putStr ","
        putStrLn (Data.Maybe.fromJust(Map.lookup "B" (excel_data !! i)))
        return =<< printExcelData (i + 1)
    where
        first_row :: Map String String
        first_row = Map.fromList [("A","佐藤"), ("B","男性")]
        second_row :: Map String String
        second_row = Map.fromList [("A","田中"), ("B","女性")]
        third_row :: Map String String
        third_row = Map.fromList [("A","小林"), ("B","女性")]
        excel_data = [first_row, second_row, third_row]

main = printExcelData 0

今回の例も、先の例と同じように、あえてインデックスで要素を取り出すような書き方にしてみています。

まずfirst_row, second_row, third_rowという、キー・値がともに文字列型の連想配列を3つ作り、Aというキー(列)に名前を、Bのキーに性別の値を入れていきます。
そして、それら全てをexcel_dataという連想配列型のリストに入れます。
その後、数値型のカウンタ変数iを0に初期セットし、リストexcel_dataの要素の数に到達するまでループで再帰的に同じ関数を呼び続け、その引数としてiを1ずつ増やしてから設定し、まわしていきます。その際に、putStr関数を使い、リストexcel_dataのi番目の要素の中のA列とB列の値を出力しています。

まとめ:Haskellは「普通」とは違う純粋関数型言語

まとめ:Haskellは「普通」とは違う純粋関数型言語

Haskellの良さを簡単にまとめると、以下のとおりです。

  • アンチパターンを実装しにくく可読性が高い ← 純粋関数型言語
  • 従来型の書き方も一部可 ← 手続き型言語のようにもよく書く
  • 色々な考え方が身に着く ← 手続き型やオブジェクト指向型とは違う書き方

つまりHaskellは、純粋関数型言語のため、アンチパターンを実装しにくく可読性が高い言語であり、従来とは違った考え方が身に着く言語です。

既にFacebookやIBM、ツイッターなどの開発で利用されていますが、一般の開発案件では正直言うとあまりHaskellが採用されることはありません。そのため、エンジニアが自分の領域や視野を広げるために学ぶのならオススメですが、初心者が一番初めにやる言語としては適していません。それなら、まだScalaを学ぶ方が良いと思います。

もしそれでもHaskellを学ばれる場合、純粋関数型で静的型付けの言語なので、初心者でなかったとしても、独学は少し難しいかもしれません。もし独学でできなさそうな方は、プログラミングスクールも手段の一つに入れると良いと思います。