Maybe自作から学ぶHaskell！

haskell Advent Calendar 2017に参加しました！！
https://qiita.com/advent-calendar/2017/haskell3

はい、というわけで！　初心者ですが頑張って書きます！

# やること
 説明を交えながら、Maybeを自作していく。
型クラスもやる。

# なぜMaybeを自作しようと思ったのか。
 Haskellを理解するには、実際に何らかの既存の型を自作してみて、体に馴染ませていくのが良いと思ったからである。

#Maybeとは
Maybeは標準で使える代数データ型の一つであり、何らかの計算が成功・失敗したかを安全に扱える便利なやつである。
計算が成功すると、Just <計算結果>を返し、
失敗なら、Nothingを返す。

#Maybeの代数データ型を定義してみる
というわけで、オリジナルのMaybe型を定義しよう。
名前は…うーん……Elipmocで！！
``` data Elipmoc a = Bat | Good a ```
実際に使用してみよう。

```haskell

data Elipmoc a = Bat | Good a 

main::IO()
main = do
    print . Good $ 2
    print . Good $ "hogehoge"
    print (Bat::Elipmoc Int)
```
ファイル名はMain.hsにした
コンパイルはめんどいのでrunghcで直接実行する。
``` $ stack runghc　Main.hs ```
実行結果

```
Main.hs:5:5: error:
    ? No instance for (Show (Elipmoc a0)) arising from a use of ‘print’
    ? In the first argument of ‘(.)’, namely ‘print’
      In the expression: print . Good
      In a stmt of a 'do' block: print . Good $ 2
以下略

```

おっとエラーになってしまったぞ。

#derivingしよう
先程の例のprint関数は内部でshow関数を使っている。
Show型クラスはオブジェクトから文字列を返すshow関数を定義しており。
ElipmocはShow型クラスのインスタンスでないので、show関数が使えずエラーとなった。
とりあえず、Maybeが持つ型クラスを調べてみることにしよう。
REPLを使用する。

```
$ stack ghci

―略―

Prelude> :i Maybe
data Maybe a = Nothing | Just a         -- Defined in ‘GHC.Base’
instance Eq a => Eq (Maybe a) -- Defined in ‘GHC.Base’
instance Monad Maybe -- Defined in ‘GHC.Base’
instance Functor Maybe -- Defined in ‘GHC.Base’
instance Ord a => Ord (Maybe a) -- Defined in ‘GHC.Base’
instance Read a => Read (Maybe a) -- Defined in ‘GHC.Read’
instance Show a => Show (Maybe a) -- Defined in ‘GHC.Show’
instance Applicative Maybe -- Defined in ‘GHC.Base’
instance Foldable Maybe -- Defined in ‘Data.Foldable’
instance Traversable Maybe -- Defined in ‘Data.Traversable’
instance Monoid a => Monoid (Maybe a) -- Defined in ‘GHC.Base’
```

Maybe型の持つ型クラスがずらずら列挙された。
この内Eq,Ord,Read,Showはderivingを使って自動でインスタンスにできるので、早速やってみる。

修正後のコード

```haskell
data Elipmoc a = Bat | Good a deriving (Eq,Ord,Read,Show)

main::IO()
main = do
    print . Good $ 2
    print . Good $ "hogehoge"
    print (Bat::Elipmoc Int)
```

実行結果

```
$ stack runghc Main.hs
Good 2
Good "hogehoge"
Bat
```
うん、ちゃんとできた。
ついでに、Eq,Ord,Readについても確かめてみよう。
その前に各種型クラスの説明をすこしだけ。

Eqは==と/=演算子が定義されていて、
等しいかどうかを調べることができる型クラスだ。

OrdはEqを継承した型クラスであり、
<と<=と>と>=演算子を定義されていて、
大小比較ができる型クラスだ。

ReadはShowクラスとは逆で、
文字列からオブジェクトを構成するread関数が定義されている。

Eq,Ord,Readも試してみよう。

```haskell
data Elipmoc a = Bat | Good a deriving (Eq,Ord,Read,Show)

main::IO()
main = do
    print (Good 2 == Good 2)
    print (Good 5 /= Good 5)
    print (Good 20 > Good 2)
    print $ Good 20 < Good 8
    print $ (read "Bat"::Elipmoc Int)
    print $ (read "Good 35"::Elipmoc Int)
```

実行結果

```
True
False
True
False
Bat
Good 35
```

# Monoid実装
Monoidは二項演算の型クラスである。

定義を示す

```haskell
class Monoid a where
    mempty :: a 
    mappend :: a -> a -> a 
    mconcat :: [a] -> a
    mconcat = foldr mappend mempty
```

memptyは単位元を返す関数で、
mappendは2つの値を結合して1つの値にする関数だ。
例としてリストで説明しよう。
リストには++という二項演算子がある。
この演算子は
```( ( [1]++[2] )++[3] )```
でも
```( [1]++( [2]++[3] ) )```
でも
結果は[1,2,3]であり、どの順番で計算しても問題無いことがわかる。
このような性質をMonoidと呼び、
haskellではMonoid型クラスのインスタンスにすることができる。
このとおり、リストはMonoid型クラスのインスタンスであるので、
上記のコードをMonoid型クラスのmappendで書き換えることができる。
```[1] `mappend` [2] `mappend` [3] ```
このように、リストでmappendを使うと++演算子と同じ働きをする。
Monoidになるためにはさらにもう一つ条件があり、それは単位元が定義できることだ。
単位元とは、``` a `mappend` e → a　```を満たすeがあることである。
例えばリストの単位元は[]である。
``` [1]++[] →　[1]　```
ほらね。
Monoid型クラスではmemptyとして定義されている。
上記をmemptyで書き換えると
``` [1] `mappend` mempty → [1] ``` 

で、MaybeもMonoidのインスタンスなので
Elipmocにも実装してみよう！

ではElipmocをMonoidのインスタンスにしてみよう。

```haskell
instance Monoid a => Monoid (Elipmoc a) where
    mempty = Bat
    Bat `mappend` m = m
    m `mappend` Bat = m
    Good m1 `mappend` Good m2 = Good (m1 `mappend` m2)
```
mconcatはデフォルトで実装されているので、
実装する必要はない。

では実際に使用してみよう。

```haskell
main::IO()
main = do
    print (Good [5] `mappend` Good [4])
    print (Bat `mappend` Good [3])
    print (Good [2] `mappend` Bat)
```

実行結果

```
Good [5,4]
Good [3]
Good [2]
```

#Functorを作ろう


Functorの定義を示す。

```haskell
class  Functor f  where
    fmap        :: (a -> b) -> f a -> f b
    (<$)        :: a -> f b -> f a
    (<$)        =  fmap . const
```

Functorには、mapという関数が定義されている。
例えば、
``` f x = 2*x ``` という関数があったとして、
``` f (Just 5) ``` のように値を渡すことはできない。5はJustで包まれているからだ。
こういうときにfmapを使うと
``` fmap　f (Just 5) ```　というふうに書くことができる。
fmapはfで包まれたaを取り出して、関数(a->b)に渡して加工して、加工したbを再びf型に包むという操作を行う。
さらにもう一つ、Functorには<$という演算子が定義されている。
<$の左辺には任意の型aを、右辺にはf型で包まれているbを渡す。
そうすると、左辺のaを右辺のf型で包んだaを返す。右辺の値は捨てられる。
例を挙げる。
``` 176 <$ Just 810 ```
右辺はMaybeで包まれているので、Just　176が返り、Just 810は捨てられる。


では、サクッと実装しますね。
<$はデフォルトで実装されるので自分で定義する必要はありません。

```haskell
instance Functor Elipmoc where
    fmap _ Bat = Bat
    fmap f (Good a) = Good (f a)
```

プログラム例

```
main::IO()
main = do
    print ( fmap (\x->[4+x]) (Good 5))
    print ( fmap (\x->[4+x]) (Bat))
```

このようにfmapに加工する関数を渡すと、Elipmocの中身を取り出し加工して再び包んで返す。

実行結果

```
Good [9]
Bat
```

#Applicativeを作ろう

Applicativeの実装を示す。

```haskell
class Functor f => Applicative f where
    pure :: a -> f a

    (<*>) :: f (a -> b) -> f a -> f b

    liftA2 :: (a -> b -> c) -> f a -> f b -> f c
    liftA2 f x = (<*>) (fmap f x)

    (*>) :: f a -> f b -> f b
    a1 *> a2 = (id <$ a1) <*> a2

    (<*) :: f a -> f b -> f a
    (<*) = liftA2 const
```

pure関数は任意のa型の値を受け取って、それをfの型で包んで返します。
<\*>演算子はfで包まれた左辺f(a->b)と任意の型aの右辺を受け取り、左辺の中身の関数(a->b)に、右辺の中身bを引数として渡し、結果をfで包んで返します。
<*演算子は左辺と右辺を受け取り、右辺を捨てて左辺を返す。
*>演算子は左辺を捨てて右辺を返す。
あと<$>演算子も紹介しよう。
<$>の定義を示す。

```haskell
(<$>) :: Functor f => (a -> b) -> f a -> f b
(<$>) = fmap
```
<$>演算子は<\*>ととても似ていて、違いは左辺がfで包まれていないだけである。
というか、ただのfmapである。
これらを使うことで見やすいコードを書くことができる。（投げやり）
じゃあ型クラス作りましょう。

```haskell
instance Applicative Elipmoc where
    pure a = Good a
    (<*>) (Good f) x = fmap f x
    (<*>) Bat _ = Bat 
```

サンプルコード

```haskell
func x y= x+y

main::IO()
main = do
    print ( func <$> Bat <*> Good 2)
    print ( func <$> Good 5 <*> Good 3 <* Good 2)
    print ( func <$> Good 5 <*> Good 3 <* Bat)
```

実行結果

```
Bat
Good 8
Bat

```

#Monadを作ろう

どんどん作っていきます

Monadの定義を示す

```haskell
class Applicative m => Monad m where
    (>>=)       :: m a -> (a -> m b) -> m b

    (>>)        :: m a -> m b -> m b
    m >> k = m >>= \_ -> k

    return      :: a -> m a
    return      = pure
```


Monadを使うことで、複数の処理を連結させて、1つの処理にまとめることができる。

ElipmocをMonadのインスタンスにする。

```haskell
instance Monad Elipmoc where
    (Good x) >>= k = k x
    Bat  >>= _ = Bat

    (Good _) >> k = k
    Bat  >>  _ = Bat

    return = Good
    fail _ = Bat

```

サンプルコード

```haskell
func 0= Bat
func x= return (x*10)

func2 = return (931::Int)

main::IO()
main = do
    print ( Good 2 >>= func)
    print ( Good 0 >>= func)
    print ( Good 1 >>= func >>= func)
    print ( Good 32 >> func2)
```

計算結果

```
Good 20
Bat
Good 100
Good 931
```

#この記事を書いた感想
うん。正直どこまで説明を細かく書くべきかわからず苦しんだよ。
モナドについてはもはや説明という説明すらない。
途中で、あれ？これ定義を書いとけば日本語でいちいち説明する必要なくね？と思ったからだ。