HaskellのEitherについて

# HaskellのEitherについて

HaskellのEitherモナドとそれに関連するData.Eitherの関数についてまとめました。この記事で使われているGHCのバージョンは8.8.3です。

## Eitherとは

Eitherとは、Haskellに標準で入っているモナド・型の1つです。GHCiで定義を見てみましょう。

```haskell
data Either a b = Left a | Right b      -- Defined in ‘Data.Either’
```

GHCiによると、Eitherは2つの値をとり、それぞれ違う2つの状態をとりえる、ということです。例外処理の際、失敗した計算を扱えます。Maybeモナドでは失敗した場合`Nothing`が返るだけで詳しい情報が得られませんが、Eitherでは失敗をより詳細に表現できます。

|              | 失敗      | 成功      |
| ------------ | --------- | --------- |
| `Maybe a`    | `Nothing` | `Just a`  |
| `Either a b` | `Left a`  | `Right b` |

失敗した場合は`Left a`、成功すれば`Right b`として値を返します。

## Eitherで遊んでみる

```haskell
ghcir> :t Right 1011
Right 1011 :: Num b => Either a b
ghci> :t Left "aaa"
Left "aaa" :: Either [Char] b
```

## Eitherの使いどころ

失敗しただけではなく、失敗した際の情報を得る(または処理を中断させない)ために使います。

`Prelude.head`を例にとります。

headはリストを引数にとり、先頭の要素を返します。ただし、リストが空だと`*** Exception: Prelude.head: empty list`という例外が発生します。

```haskell
ghci> head "abcdef"
'a'
ghci> head [1,4,5,2]
1
ghci> head []
`*** Exception: Prelude.head: empty list`
```

これをそのまま実装すると、以下のコードになります。

``` haskell
head :: [a] -> a
head [] = errorWithoutStackTrace "empty list"
head (x:xs) = x
```

 これをEitherを使って実装してみます。

```haskell
head' :: [a] -> Either String a
head' [] = Left "empty list"
head' (x:xs) = Right x
```

正しく動作しているのがわかります。

```haskell
ghci> head' [1,3,2]
Right 1
ghci> head' "aaaaa"
Right 'a'
ghci> head' []
Left "empty list"
```

この`Right x`から`x`を取り出す方法はData.Eitherで紹介します。

## Data.Either

EitherはData.Eitherで定義されています。Data.Eitherには、以下のようなEitherを扱う関数があります。

```haskell
either :: (a -> c) -> (b -> c) -> Either a b -> c
fromLeft :: a -> Either a b -> a
fromRight :: b -> Either a b -> b
isLeft :: Either a b -> Bool
isRight :: Either a b -> Bool
lefts :: [Either a b] -> [a]
partitionEithers :: [Either a b] -> ([a], [b])
rights :: [Either a b] -> [b]
```

1つ1つ見ていきましょう。

### either

```haskell
either :: (a -> c) -> (b -> c) -> Either a b -> c
```

`either`はPreludeに含まれていて、関数を2つとEither a b型の値をとります。引数の値が`Left`か`Right`で処理を分けたいときに使えます。

```haskell
ghci> let x = Right 1234 :: Either String Int
ghci> let y = Left "left" :: Either String Int
ghci> either length (*3) x
3702
ghci> either length (*3) y
4
```

ちなみに、1行目と2行目の束縛での型注釈は必須ではありません。型注釈をつけなかった場合、xとyの型は以下のようになります。

```haskell
x :: Num b => Either a b
y :: Either [Char] b
```

このとき、xの`Either a`とyの`Either b`の型をコンパイラは知りませんが、このまま`either length (*3)`に突っ込んでもエラーは発生せず、上と同じ結果が得られます。なぜなら、eitherは値がLeftかRightかわかった時点でもう片方の関数は評価しないからです。なので、eitherがとる値はLeftかRightであればよいのです。

### fromLeft, fromRight

```haskell
fromLeft :: a -> Either a b -> a
fromRight :: b -> Either a b -> b
```

fromLeftはLeftから、fromRightはRightから値を取り出します。fromLeftはEitherの値がRightだった時の値を、fromRightはEitherの値がLeftだった時の値をそれぞれとります。ただし、2つの引数の型は同じ(Left Int → Int、Right String → String)である必要があります。

```haskell
ghci> fromLeft "bananas" (Left "apple")
"apple"
ghci> fromLeft 493 (Right 34141)
493
```

### isLeft, isRight

```haskell
isLeft :: Either a b -> Bool
isRight :: Either a b -> Bool
```

それぞれ、引数の型がLeft、RightであればTrue、そうでなければFalseを返します。

```haskell
ghci> isLeft (Left "aaa")
True
ghci> isRight (Right 381)
True
```

### lefts, rights

```haskell
lefts :: [Either a b] -> [a]
rights :: [Either a b] -> [b]
```

Eitherのリストから、それぞれLeft、Rightの値をフィルタします。

```haskell
ghci> let ls = [Right 3,Left "apple",Right 4, Left "bananas",Right 10]
ghci> lefts ls
["apple","bananas"]
ghci> rights ls
[3,4,10]
```

### partitionEithers

```haskell
partitionEithers :: [Either a b] -> ([a], [b])
```

lefts、rightsで取り出した値を、1つのタプルに格納します。

```haskell
ghci> partitionEithers ls
(["apple","bananas"],[3,4,10])
```

## EitherとFunctor、Applicative

もうお気づきの方もいると思いますが、Eitherは実はFunctorのインスタンスです。[^1]

```haskell
instance Functor (Either a) -- Defined in ‘Data.Either’
```

`ghc/libraries/base/Data/Either.hs`に定義があります。

```haskell
instance Functor (Either a) where
    fmap _ (Left x) = Left x
    fmap f (Right y) = Right (f y)
```

fmapにRightの値を与えると関数が適用され、Leftの値を与えるとそのまま返ってくる、ということです。

```haskell
ghci> fmap (*3) (Left 2)
Left 2982
ghci> fmap (*3) (Right 2)
Right 6
ghci> (*3) <$> Right 4
Right 12
ghci> (*3) <$> Left 4
Left 4
```

また、EitherはApplicativeのインスタンスでもあるので、アプリカティブ・スタイルでコードを書くこともできます。

```haskell
instance Applicative (Either e) where
    pure          = Right
    Left  e <*> _ = Left e
    Right f <*> r = fmap f r
```

pureの引数はRightで返ってきます。`<*>`の右辺の値がLeftならそれが返ってきて、Rightなら関数が適用されて返ってきます。

```haskell
ghci> pure 3 :: Either String Int
Right 3
ghci> Right (*3) <*> Left 3
Left 3
ghci> Right (*3) <*> Right 3
Right 9
ghci> Left 3 <*> Right 3
Left 3
ghci> Left 3 <*> Left 3
Left 3
```

記事の先頭で書いたとおり、EitherはモナドなのでMonadのインスタンスとして定義があります。

```haskell
instance Monad (Either e) where
    Left  l >>= _ = Left l
    Right r >>= k = k r
```

`>>=`にLeftの値を突っ込むとそのまま、Rightの値は関数が適用されて返ってくる、ということです。

Functor、Applicative、MonadそれぞれLeftなら値がスルーされ、Rightなら評価されるという認識でよいかと思います。

## 参考文献

- [GHC - GitHub](https://github.com/ghc/ghc)

- [Data.Either - Hackage](https://hackage.haskell.org/package/base-4.12.0.0/docs/Data-Either.html)

- [箱で考えるFunctor、ApplicativeそしてMonad - @suinさん](https://qiita.com/suin/items/0255f0637921dcdfe83b)

## 注釈

[^1]: Functor、Applicative、Monadについては上の記事が非常にわかりやすいので読んでいただければと思います。