Haskell 入門してみた

この記事は[ユニークビジョン株式会社 Advent Calendar 2021 10日目](https://qiita.com/advent-calendar/2021/uniquevision)の記事です。

関数型言語に興味があり、勉強してみたいと考えています。
直接的には業務で使用しない内容のため、勉強会で発表したり Qiita に投稿したりして勉強するモチベーションにしていけたらいいなと思い、今回のテーマに選びました。

（今回は勉強が間に合わず浅めの内容となっております）

関数型プログラミングについて、[ウィキペディア](https://ja.wikipedia.org/wiki/%E9%96%A2%E6%95%B0%E5%9E%8B%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0)より:

> 関数型プログラミングは、関数を主軸にしたプログラミングを行うスタイルである。ここでの関数は、数学的なものを指し、引数の値が定まれば結果も定まるという参照透過性を持つものである。

> 関数型プログラミング言語とは、関数型プログラミングを推奨しているプログラミング言語である。略して関数型言語ともいう。

## 関数型言語の特徴

### 参照透過性

式の構成要素がすべて同じなら、式の値は常に同じになるということ

→ 式の値を計算するのに、その時点での状態を考慮する必要がない

##### （参照透過性に関係のある言葉:）

-   副作用
    -   ある機能がコンピュータの状態を変化させ、それ以降の結果に影響を与える作用
-   純粋関数
    -   関数の結果は入力のみに依存する
    -   副作用を引き起こさない
    -   関数自身のスコープの外にある値に影響を及ぼさない

## 関数型言語の例

[ウィキペディア](https://ja.wikipedia.org/wiki/%E9%96%A2%E6%95%B0%E5%9E%8B%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0) より:

<table>
<thead><tr>
  <th>
    名前
  </th>
  <th>
    型付け
  </th>
  <th>
    純粋性
  </th>
  <th>
    評価戦略
  </th>
  <th>
    理論的背景
  </th>
</tr></thead>
<tbody>
<tr>
<td>Clean
</td>
<td>静的型付け
</td>
<td>純粋
</td>
<td>遅延評価
</td>
<td>
</td></tr>
<tr>
<td>Clojure
</td>
<td>動的型付け
</td>
<td>非純粋
</td>
<td>正格評価
</td>
<td>
</td></tr>
<tr>
<td>Erlang
</td>
<td>動的型付け
</td>
<td>非純粋
</td>
<td>正格評価
</td>
<td>
</td></tr>
<tr>
<td>F#
</td>
<td>静的型付け
</td>
<td>非純粋
</td>
<td>正格評価
</td>
<td>
</td></tr>
<tr>
<td><b>Haskell</b>
</td>
<td>静的型付け
</td>
<td>純粋
</td>
<td>遅延評価
</td>
<td>型付きラムダ計算
</td></tr>
<tr>
<td>Idris
</td>
<td>静的型付け
</td>
<td>純粋
</td>
<td>正格評価
</td>
<td>型付きラムダ計算
</td></tr>
<tr>
<td>Lazy K
</td>
<td>型なし
</td>
<td>純粋
</td>
<td>遅延評価
</td>
<td>コンビネータ論理
</td></tr>
<tr>
<td>LISP
</td>
<td>動的型付け
</td>
<td>非純粋
</td>
<td>方言による
</td>
<td>型無しラムダ計算
</td></tr>
<tr>
<td>Miranda
</td>
<td>静的型付け
</td>
<td>純粋
</td>
<td>遅延評価
</td>
<td>
</td></tr>
<tr>
<td>ML
</td>
<td>静的型付け
</td>
<td>非純粋
</td>
<td>先行評価
</td>
<td>
</td></tr>
<tr>
<td>SML
</td>
<td>静的型付け
</td>
<td>非純粋
</td>
<td>正格評価
</td>
<td>
</td></tr>
<tr>
<td>OCaml
</td>
<td>静的型付け
</td>
<td>非純粋
</td>
<td>正格評価
</td>
<td>
</td></tr>
<tr>
<td>Scala
</td>
<td>静的型付け
</td>
<td>非純粋
</td>
<td>正格評価
</td>
<td>
</td></tr>
<tr>
<td>Scheme
</td>
<td>動的型付け
</td>
<td>非純粋
</td>
<td>正格評価
</td>
<td>
</td></tr>
<tr>
<td>Unlambda
</td>
<td>型なし
</td>
<td>非純粋
</td>
<td>正格評価
</td>
<td>コンビネータ論理
</td></tr></tbody><tfoot></tfoot></table>

## Haskell の特徴

-   簡潔なプログラム
-   強力な型システム
    -   多層性と多重定義を許している
-   リスト内包表記
-   再帰関数
    -   再帰関数を使って繰り返しを実現する
-   パターンマッチ・ガードを使える
-   高階関数
-   作用を持つ関数
    -   副作用が必要なときに、関数の純粋性を犠牲にしないアプリカティブやモナドに基づいた機構を提供している
-   汎用的な関数
-   遅延評価
-   等式推論

```hs
qsort :: Ord a => [a] -> [a]
qsort [] = []
qsort (x:xs) = qsort smaller ++ [x] ++ qsort larger
  where
      smaller = [a | a <- xs, a <= x]
      larger = [b | b <- xs, b > x]
```

### 第一級関数

関数を第一級オブジェクトとして扱うことのできる関数のこと

-   第一級オブジェクト:
    -   リテラルとして表現可能な値で、変数への格納や引数や戻り値として受け渡しが行えるようなオブジェクトのこと

```hs
double x = x * 2
map double [1, 2, 3] -- [2, 4, 6]
```

（`map` 関数は、第一引数で関数を受け取り、第二引数で受け取ったリストの各要素に対して関数を実行した結果を返す）

### 遅延評価

結果が必要になるまで関数を実行しない、という性質。

例えば、無限リストから先頭の 100 件を取り出すことができる。

```hs
> sum (take 100 [1 ..])
5050
```

### Haskell の型の表記

```hs
mul :: Int -> Int -> Int
mul x y = x * y
```

型クラスを使って一般化することができる。（`Num` は数値型全般を表す型クラス）

```hs
mul2 :: Num a => a -> a -> a
mul2 x y = x * y
```

型クラスは型の振る舞いを定義するもの。

（GHCi では `:info`（`:i`）コマンドでその名前の定義情報などを出力できる）

```hs
Prelude> :i Num
type Num :: * -> Constraint
class Num a where
  (+) :: a -> a -> a
  (-) :: a -> a -> a
  (*) :: a -> a -> a
  negate :: a -> a
  abs :: a -> a
  signum :: a -> a
  fromInteger :: Integer -> a
  {-# MINIMAL (+), (*), abs, signum, fromInteger, (negate | (-)) #-}
        -- Defined in ‘GHC.Num’
instance Num Word -- Defined in ‘GHC.Num’
instance Num Integer -- Defined in ‘GHC.Num’
instance Num Int -- Defined in ‘GHC.Num’
instance Num Float -- Defined in ‘GHC.Float’
instance Num Double -- Defined in ‘GHC.Float’
```