Haskell入門から学ぶ～型～

#### Haskell入門 第3章から抜粋

##### 型の記述
定義：`式::型`

```hs
Prelude> -- 式::型
Prelude> 1::Int
1
```

```hs 
intToArray :: Int -> Int -> [Int]
intToArray i l = take l (repeat i)

main :: IO ()
main = do
    print (intToArray 3 4) -- [3, 3, 3, 3]
    -- print (intToArray 5000 12.04) -- 第二引数がIntじゃないのでコンパイルエラー
    -- print (intToArray 'a' 100) -- 第一引数がIntじゃないのコンパイルでエラー
```

```hs
Example>stack ghc -- Main.hs
[1 of 1] Compiling Main             ( Main.hs, Main.o )
Linking Main.exe ...

Example>Main.exe
[3,3,3,3]
```
##### 型のチェック
コマンド：`:type 識別子` or `:t 識別子`
上記コマンドで識別子の型を確認することができる。
関数であれば、引数の型と戻り値の型の型がわかる。

```hs
Prelude> f x = x / 3
Prelude> :t f
f :: Fractional a => a -> a
Prelude> -- Fractionalクラスのa型を引数にとりa型を返す
Prelude> -- 関数をコール
Prelude> f 10
3.3333333333333335
Prelude> -- 型を明示してコール
Prelude> f 10 :: Double
3.3333333333333335
```
##### 型コンストラクタと型変数
Int, Doubule, Stringなどの型のことを正確には`型コンストラクタ`と呼ぶ。
`型コンストラクタ`には単体で使うものと`型引数`をとるものがある。

###### 型の組み立て
`型コンストラクタ`を組み合わせることでカスタム型を組み立てていく。
`Integer`や`Bool`のような単体で型として成立するものあれば、`IO`や`Maybe`のように他の型を引数に持つ型もある。
こうした型の特性は`カインド(kind)`と呼ばれる。

##### カインド(kind)
`カインド(kind)`は`*(star)`と`->`の組み合わせで表現される。

| カインド(kind)|   説明    |例|
|:-----------------|:---|:---|
| *| 単体で型として成立  |Int, String, Bool|
| * -> * | 型と型引数を持つ  |Maybe, IO, []|

```hs

Prelude> :kind Int
Int :: *
Prelude> :k String
String :: *
Prelude> :k Maybe
Maybe :: * -> *
Prelude> :k IO
IO :: * -> *
Prelude> :k []
[] :: * -> *
```

##### 型変数(type variables)
``:type`コマンドで型を確認した際の`a`, `b`, `f`などの変数のこと。

```hs

Prelude> -- idは何かしらの型を引数にとり、引数と同じ型を返す
Prelude> :t id
id :: a -> a
Prelude> -- リストを引数にとり、リストの値を返す
Prelude> :t head
head :: [a] -> a
Prelude> -- Showクラスの型を引数にとり、String型を返す
Prelude> :t show
show :: Show a => a -> String
Prelude> -- Functorのインスタンス型aへ関数を適用した結果をFunctorのインスタンス型bを返す
Prelude> :t fmap
fmap :: Functor f => (a -> b) -> f a -> f b
Prelude> -- fmapをリストに実装
Prelude> fmAdd = fmap :: (Int -> Int) -> [Int] -> [Int]
Prelude> fmAdd (+10) [1, 2, 3]
[11,12,13]
Prelude> -- fmapをMaybeに実装
Prelude> maybeFmap = fmap :: (a -> b) -> Maybe a -> Maybe b
Prelude> maybeFmap (* 10) (Just 10)
Just 100
```

##### 代数的データ型
代数的データ型(algebraic data type)を用いてカスタムデータ型を定義することができる。

定義：`data 型コンストラクタ名 = データコンストラクタ名 フィールドの型1 フィールドの型2 ...`

- データコンストラクタ名
データコンストラクタ名は型コンストラクタ名と同じく大文字から始まる識別子を使う。慣例的に型コンストラクタど同名を用いることが多い。

```hs
Prelude> -- 代数的データ型の定義
Prelude> data Employee = Employee Integer Bool String deriving (Show)
Prelude> -- データコンストラクタのタイプチェック
Prelude> :t Employee
Employee :: Integer -> Bool -> String -> Employee
Prelude> -- Employee型のデータを生成しempへバインド
Prelude> emp = Employee 25 False "Uchino Tamako"
Prelude> emp
Employee 25 False "Uchino Tamako"
Prelude> -- パターンマッチを使ってフィールドへアクセスする
Prelude> Employee age isManager name = emp
Prelude> age
25
Prelude> isManager
False
Prelude> name
"Uchino Tamako"
```
##### ガードを用いたデータコンストラクタの定義
１つの型コンストラクタに対して複数のデータコンストラクタを定義することができる。

定義：`data 型コンストラクタ = データコンストラクタ1 フィールド1
| データコンストラクタ2 フィールド1 フィールド2`

- 実装例

```hs
Prelude> :{
Prelude| data CmdOption = COptInt Integer
Prelude|                | COptBool Bool
Prelude|                | COptString String
Prelude|                deriving (Show)
Prelude| :}
Prelude> :t COptInt
COptInt :: Integer -> CmdOption
Prelude> optInt = COptInt 10
Prelude> optString = COptString "Option"
Prelude> :t optInt
optInt :: CmdOption
Prelude> :t optString
optString :: CmdOption
```

##### レコード記法
型が持つフィールドにフィールド名をつけて定義する手法。
定義：'data 型コンストラクタ名 = データコンストラクタ名 { フィールド名1:: 型1, フィールド名2:: 型2}'

```hs
Prelude> -- レコード記法で代数的データ型定義
Prelude> data Employee = NewEmployee { employeeAge::Integer, employeeManager::Bool, employeeName::String} deriving (Show)
Prelude> -- 定義した型を利用する
Prelude> :{
Prelude| emp :: Employee
Prelude| emp = NewEmployee { employeeAge = 20, employeeManager = False, employeeName = "Sato" }
Prelude| :}
Prelude> :{
Prelude| emp :: Employee
Prelude| emp = NewEmployee { employeeAge = 20, employeeManager = False, employeeName = "Sato" }
Prelude| :}
Prelude> --　employeeAgeの型を:typeで調べる
Prelude> :t employeeAge
employeeAge :: Employee -> Integer
Prelude> --　年齢を取得
Prelude> employeeAge emp
20
Prelude> --　名前を取得
Prelude> employeeName emp
"Sato"
```

##### 値の更新
代数的データ型の値を更新する例

```hs
Prelude> :{
Prelude| emp_ :: Employee
Prelude| emp_ = emp { employeeAge = employeeAge emp + 1}
Prelude| :}
Prelude> -- 年齢を確認
Prelude> employeeAge emp_
21
Prelude> employeeAge emp
20
```

##### 型の別名

定義：`type 型名 = 型`
定義：`newtype 型コンストラクタ名 = データコンストラクタ名`
`type`キーワードで作られる型は元の型と同じだが、`newtype`キーワードで作成された型は元の型を同じ型を新しく作る点が異なる。