[Haskell] モナド 演算子 まとめ

本記事ではモナドを操作する演算子について書きます。

参考「[Data.Functor](https://hackage.haskell.org/package/base-4.19.0.0/docs/Data-Functor.html)」
参考「[Control.Applicative](https://hackage.haskell.org/package/base-4.19.0.0/docs/Control-Applicative.html)」
参考「[Control.Monad](https://hackage.haskell.org/package/base-4.19.0.0/docs/Control-Monad.html)」

# 0. まとめ

用途別:

- 値に関数を適用する
    - `<$>`, `<&>`
    - `<*>`, `<**>`
    - `=<<`, `>>=`
    - `<$!>`
- 関数同士を合成する
    - `<=<`, `>=>`
- 値を破棄して他の値に置き換える
    - `<$`, `$>`
    - `<*`, `*>`
    - `>>`
- 値同士を結合する
    - `<|>`

型クラス制約別:

- `Functor`
    - `<$>`, `<&>`
    - `<$`, `$>`
- `Applicative`
    - `<*>`, `<**>`
    - `<*`, `*>`
- `Monad`
    - `=<<`, `>>=`
    - `<=<`, `>=>`
    - `>>`
    - `<$!>`
- `Alternative`
    - `<|>`

※ `>>` は歴史的な理由により残されていますが、`(>>) = (*>)` です。
※ `<$!>` は `<$>` の正格版です。

ここでは説明のため各演算子の型クラス制約を略しますが、型変数 `f` の型は `Functor`, `Applicative`, `Alternative` クラスのインスタンス、型変数 `m` の型は `Monad` クラスのインスタンスです。

```haskell
($)    ::   (a ->   b) ->   a ->   b -- (比較用)
(<$>)  ::   (a ->   b) -> f a -> f b -- Functor
(<*>)  :: f (a ->   b) -> f a -> f b -- Applicative
(=<<)  ::   (a -> m b) -> m a -> m b -- Monad

(&)    ::   a ->   (a ->   b) ->   b -- (比較用)
(<&>)  :: f a ->   (a ->   b) -> f b -- Functor
(<**>) :: f a -> f (a ->   b) -> f b -- Applicative
(>>=)  :: m a ->   (a -> m b) -> m b -- Monad
```

```haskell
(.)   :: (b ->   c) -> (a ->   b) -> a ->   c -- (比較用)
(<=<) :: (b -> m c) -> (a -> m b) -> a -> m c -- Monad

(>=>) :: (a -> m b) -> (b -> m c) -> a -> m c -- Monad
```

```haskell
const ::   a ->   b ->   a -- (比較用)
(<$)  ::   a -> f b -> f a -- Functor
(<*)  :: f a -> f b -> f a -- Applicative

($>)  :: f a ->   b -> f b -- Functor
(*>)  :: f a -> f b -> f b -- Applicative
```

```haskell
(<>)  ::   a ->   a ->   a -- (比較用)
(<|>) :: f a -> f a -> f a -- Alternative
```

# 1. 値に関数を適用する

## 1.1. 一般: `$`, `&`

`a` 型の値に `a -> b` 型の関数を適用して `b` 型の値を得ます。

```haskell
import Data.Function ((&))

f :: Int -> Int
f = (* 42)

g :: Int -> Int
g = subtract 1729

main :: IO ()
main = do

    let x = 23 :: Int

    -- 
    print (f x :: Int)

    print (g . f $ x :: Int)

    -- 
    print (x & f :: Int)

    print (x & f & g :: Int)
```

関数の引数が複数ある場合は以下のようになります。

```haskell
import Data.Function ((&))

f :: Int -> Int -> Int -> Int
f x y z = x * y - z

main :: IO ()
main = do

    let x = 23   :: Int
    let y = 42   :: Int
    let z = 1729 :: Int

    print (f x y z :: Int)

    print (z & (y & (x & f)) :: Int)
```

## 1.2. `Functor`: `<$>`, `<&>`

インスタンス `Functor f` に関して、`f a` 型の値に `a -> b` 型の関数を適用して `f b` 型の値を得ます。

```haskell
import Data.Functor ((<&>))

f :: Int -> Int
f = (* 42)

g :: Int -> Int
g = subtract 1729

main :: IO ()
main = do

    let x = Just 23 :: Maybe Int

    -- 
    print (f <$> x :: Maybe Int)

    print (g . f <$> x :: Maybe Int)

    -- 
    print (x <&> f :: Maybe Int)

    print (x <&> f <&> g :: Maybe Int)
```

関数の引数が複数ある場合は以下のようになります。

```haskell
import Control.Applicative ((<**>), liftA2, liftA3)
import Data.Functor ((<&>))

f :: Int -> Int -> Int -> Int
f x y z = x * y - z

main :: IO ()
main = do

    let x = Just 23   :: Maybe Int
    let y = Just 42   :: Maybe Int
    let z = Just 1729 :: Maybe Int

    -- 
    print (f <$> x <*> y <*> z :: Maybe Int)

    print (liftA3 f x y z     :: Maybe Int)
    print (liftA2 f x y <*> z :: Maybe Int)

    -- 
    print (z <**> (y <**> (x <&> f)) :: Maybe Int)
```

※ `liftA2` 関数および `liftA3` 関数については別記事参照。

参考「[[Haskell] f <$> x <*> y よりも liftA2 f x y の方が計算コストを抑えられることがある - Qiita](https://qiita.com/kerupani129/items/c94f389c68a45f1e64d3)」

## 1.3. `Applicative`: `<*>`, `<**>`

インスタンス `Applicative f` に関して、`f a` 型の値に `f (a -> b)` 型の関数を適用して `f b` 型の値を得ます。

```haskell
import Control.Applicative ((<**>))

f :: Maybe (Int -> Int)
f = Just (* 42)

g :: Maybe (Int -> Int)
g = Just $ subtract 1729

main :: IO ()
main = do

    let x = Just 23 :: Maybe Int

    -- 
    print (f <*> x :: Maybe Int)

    print (g <*> (f <*> x) :: Maybe Int)

    -- 
    print (x <**> f :: Maybe Int)

    print (x <**> f <**> g :: Maybe Int)
```

関数の引数が複数ある場合は以下のようになります。

```haskell
import Control.Applicative ((<**>))

f :: Maybe (Int -> Int -> Int -> Int)
f = Just $ \x y z -> x * y - z

main :: IO ()
main = do

    let x = Just 23   :: Maybe Int
    let y = Just 42   :: Maybe Int
    let z = Just 1729 :: Maybe Int

    print (f <*> x <*> y <*> z :: Maybe Int)

    print (z <**> (y <**> (x <**> f)) :: Maybe Int)
```

## 1.4. `Monad`: `=<<`, `>>=`

インスタンス `Monad m` に関して、`m a` 型の値に `a -> m b` 型の関数を適用して `m b` 型の値を得ます。

```haskell
import Control.Monad ((<=<), (>=>))

f :: Int -> Maybe Int
f = Just . (* 42)

g :: Int -> Maybe Int
g = Just . subtract 1729

main :: IO ()
main = do

    let x = Just 23 :: Maybe Int

    -- 
    print (f =<< x :: Maybe Int)

    print (g =<< f =<< x :: Maybe Int)

    print ((g <=< f) =<< x :: Maybe Int)

    -- 
    print (x >>= f :: Maybe Int)

    print (x >>= f >>= g :: Maybe Int)

    print (x >>= (f >=> g) :: Maybe Int)
```

上記のコードを `do` 記法の `<-` を用いて書くと以下のようになります。

```haskell
f :: Int -> Maybe Int
f = Just . (* 42)

g :: Int -> Maybe Int
g = Just . subtract 1729

main :: IO ()
main = do

    let x = Just 23 :: Maybe Int

    print $ do
        y :: Int <- x :: Maybe Int
        f y :: Maybe Int

    print $ do
        y :: Int <- x   :: Maybe Int
        z :: Int <- f y :: Maybe Int
        g z :: Maybe Int
```

# 2. 関数同士を合成する

## 2.1. 一般: `.`

`a -> b` 型の関数と `b -> c` 型の関数を合成して `a -> c` 型の関数を得ます。

```haskell
f :: Int -> Int
f = (* 42)

g :: Int -> Int
g = subtract 1729

main :: IO ()
main = do

    let x = 23 :: Int

    print $ (g . f :: Int -> Int) x
```

## 2.2. `Monad`: `<=<`, `>=>`

インスタンス `Monad m` に関して、`a -> m b` 型の関数と `b -> m c` 型の関数を合成して `a -> m c` 型の関数を得ます。

```haskell
import Control.Monad ((<=<), (>=>))

f :: Int -> Maybe Int
f = Just . (* 42)

g :: Int -> Maybe Int
g = Just . subtract 1729

main :: IO ()
main = do

    let x = Just 23 :: Maybe Int

    print $ (g <=< f :: Int -> Maybe Int) =<< x

    print $ x >>= (f >=> g :: Int -> Maybe Int)
```

上記のコードを `do` 記法の `<-` を用いて書くと以下のようになります。

```haskell
f :: Int -> Maybe Int
f = Just . (* 42)

g :: Int -> Maybe Int
g = Just . subtract 1729

main :: IO ()
main = do

    let x = Just 23 :: Maybe Int

    print
        $ (\y -> do
            z :: Int <- f y :: Maybe Int
            g z :: Maybe Int
        )
        =<< x

    print $ x >>= \y -> do
        z :: Int <- f y :: Maybe Int
        g z :: Maybe Int
```

# 3. 値を破棄して他の値に置き換える

## 3.1. 一般: `const`

`a` 型の値と `b` 型の値から、`b` 型の値を破棄して `a` 型の値を得ます。

```haskell
f :: Int -> () -> Int
f = const

main :: IO ()
main = do

    let x = 23 :: Int
    let y = () :: ()

    print (x `f` y :: Int)
```

## 3.2. `Functor`: `<$`, `$>`

インスタンス `Functor f` に関して、`a` 型の値と `f b` 型の値から、`b` 型の値を破棄して `f a` 型の値を得ます。

```haskell
import Data.Functor (($>))

main :: IO ()
main = do

    let x = 23      :: Int
    let y = Just () :: Maybe ()

    print (x <$ y :: Maybe Int)

    print (y $> x :: Maybe Int)
```

## 3.3. `Applicative`: `<*`, `*>`

インスタンス `Applicative f` に関して、`f a` 型の値と `f b` 型の値から、`b` 型の値を破棄して `f a` 型の値を得ます。

```haskell
main :: IO ()
main = do

    let x = Just 23 :: Maybe Int
    let y = Just () :: Maybe ()

    print (x <* y :: Maybe Int)

    print (y *> x :: Maybe Int)
```

インスタンス `Monad m` の場合、上記のコードを `do` 記法を用いて書くと以下のようになります。

```haskell
main :: IO ()
main = do

    let x = Just 23 :: Maybe Int
    let y = Just () :: Maybe ()

    print $ do
        y :: Maybe ()
        x :: Maybe Int
```

## 3.4. `const` と `<*` の違い

インスタンス `Applicative f` に関して、`f a` 型の値と `f b` 型の値から `f a` 型の値を得るとき、`f b` 型の値を破棄するか `b` 型の値を破棄するかが異なります。

```haskell
f :: Maybe Int -> Maybe () -> Maybe Int
f = const

main :: IO ()
main = do

    let x = Just 23 :: Maybe Int
    let y = Nothing :: Maybe ()

    print (x `f` y :: Maybe Int)

    print (x <* y :: Maybe Int)
```

```text:実行結果
Just 23
Nothing
```

# 4. 値同士を結合する

## 4.1. `Semigroup`: `<>`

インスタンス `Semigroup a` に関して、`a` 型の値 2 つを結合します。

```haskell
main :: IO ()
main = do

    print $ ([23] :: [Int]) <> ([42] :: [Int])

    print $ (Right 23 :: Either String Int) <> (Right 42 :: Either String Int)

    print $ (() :: ()) <> (() :: ())

    print =<< (pure () :: IO ()) <> (pure () :: IO ())
```

## 4.2. `Alternative`: `<|>`

全ての `Monad` クラスのインスタンスで使えるわけではありませんが、主に `MonadPlus` クラスのインスタンスに関して `<|>` を使うことができます。

(※ `Monad` でも `MonadPlus` でもない `Alternative` クラスのインスタンスである `ZipList` 型のような例外も存在します。)

インスタンス `Alternative f` に関して、`f a` 型の値 2 つを結合します。

```haskell
import Control.Applicative ((<|>))

main :: IO ()
main = do

    print $ ([23] :: [Int]) <|> ([42] :: [Int])

    print $ (Just 23 :: Maybe Int) <|> (Just 42 :: Maybe Int)

    print =<< (pure () :: IO ()) <|> (pure () :: IO ())

    print =<< (pure 23 :: IO Int) <|> (pure 42 :: IO Int)
```

## 4.3. `<>` と `<|>` の違い

`<>` と `<|>` は動作は非常に似ていますが、使用できる型が異なります。

`List` 型に関しては `Semigroup` クラスと `Alternative` クラスの両方のインスタンスのため、実際に `(<>) = (++)` かつ `(<|>) = (++)` という全く同じ動作をします。

参考「[Data.List](https://hackage.haskell.org/package/base-4.19.0.0/docs/Data-List.html)」