Monadクラスの >>= はどのような場面で使いますか？


# 前置き

`Functor`や`Monad`を理解するためには`コンテキスト`を理解することが大切と考えます。  
`コンテキスト`については下記記事のように言及しているものがあります（ほかにもたくさんあります）。

**引用:**

[Functor - HaskellWiki](https://wiki.haskell.org/Functor)

> The Functor typeclass represents the mathematical functor: a mapping between
> categories in the context of category theory. In practice a functor
> represents a type that can be mapped over.

[SwiftのOptional型を極める -
Qiita](https://qiita.com/koher/items/c6f446bad54442a28bf4#%E3%83%A2%E3%83%8A%E3%83%89%E3%81%A8%E3%81%97%E3%81%A6%E3%81%AEoptional)

>
> モナドというのは、モナドでくるまれた中の世界ではモナドを気にせずに処理が記述でき、外からみるとモナドでくるまれているという、外と中を分離するための仕組みです。

## コンテキストの私の理解

さて `コンテキスト`
という言葉はそれこそ"文脈"によって意味がすこし変わってきそうですが（Perlにも`コンテキスト`という考えがありますし）、Hakellに置いて`コンテキスト`というと、"一度その状態（コンテキスト）に入ったらその影響を外部に漏らしたくない"
というような意味合いだと思っています。

# >>= を使いたい場面とは？

さて、ここになんのヘンテツもない 引数を受け取って+1して返す関数を定義します

[code]

    inc x = x + 1
    
[/code]

これを `Functor` を使えば、`Maybe`コンテキストの仲間に加えることができます。

[code]

    hoge = Just 1
    inc <$> hoge
    
[/code]

一方 Monad を使うのであれば、同じように +1 して返す関数は下記になると考えます。

[code]

    incWrapedJust x = Just(x + 1)
    
[/code]

そして使い方は

[code]

    hoge = Just 1
    hoge >>= incWrapedJust
    
[/code]

こんな感じ。

でも、 **ここで疑問に思いました** 。

**コンテキストのことを意識しているプログラマなのであれば、わざわざこんな引数と返り値でコンテキストがチグハグな関数を作るでしょうか？**

[code]

    hoge = Just 1
    incJustJust (Just x) = Just(x + 1)
    incJustJust hoge
    
[/code]

こんな風に引数と返り値で同じコンテキストの関数を作って使えば済む話ではないでしょうか？

## 疑問

  * コンテキストのことを意識しているのであれば、なぜわざわざこんなチグハグな関数をつくるのでしょうか？ 
    * 作りたい、使いたいタイミングはあるのでしょうか？

たんなる疑問ではお答えしづらいかと思いますので、念の為に回答として期待することを書きます。

たとえば「Haskellでよくつかわれているhogehogeという関数はこういったチグハグな関数なので、使わざるを得ない」や「fugafugaといった場面ではチグハグな関数を作って使いたいことがある」等です。（もちろん他の私の想像もしていないような回答でもよいと思います）

* * *

追記:

[@YAMAMOTO
Yujiさんの回答](https://ja.stackoverflow.com/a/70052/9008)でチグハグな関数が生まれる（使いたい）場面がとてもよくわかりました。
実際に試してみようと下記コードを書いてみました。

[code]

    import qualified Data.Map as Map 
    
    dict = Map.fromList [("key1", "val1"), ("key2", "val2")]
    f x = Just ("Mr." ++ x)
    
    Map.lookup "key1" dict >>= f -- Just "Mr.val1"
    
[/code]

これで確かにチグハグな関数が生まれる例はわかったのですが、 `>>=` を使いたい場面がまだわからないままです。上記コードの `f関数` は
私がはじめに書いた `incWrapedJust x = Just(x + 1)` くらい現実的ではない関数に感じます。

たとえば、 `key1`で取り出した値をさらに別のキーにして取り出すのであれば多少現実的かなと思い下記のコードを書いてみたのですが、

[code]

    import qualified Data.Map as Map 
    
    dict = Map.fromList [("key1", "key2"), ("key2", "val2")]
    Map.lookup "key1" dict >>= Map.lookup dict
    
[/code]

エラーになってしまいました。（上記のコードを成功させたいわけではなく、 `>>=`
と処理がつながっていく自然な例が知りたいです。上記が動けば、同じデータ構造からそんな検索かけることあるかよって気持ちにもなりますが、すこしは自然なコードかと思います。）




> 一方 Monad を使うのであれば、同じように +1 して返す関数は下記になると考えます。
[code]

>     incWrapedJust x = Just(x + 1)
>  
[/code]

この例があまり現実的ではないので、確かにこのような関数は定義しないでしょう。

# `Maybe`における「チグハグな関数」例

実際のところ、仰るような「チグハグな関数」は現実世界にはたくさんあります。  
典型的なのは[containersパッケージの`Data.Map.Strict`モジュール、`lookup`関数](http://hackage.haskell.org/package/containers-0.6.3.1/docs/Data-
Map-Strict.html#v:lookup)です。

この関数は、`Map`型という連想配列（RubyでHashと呼ばれるものに相当します。SwiftではDictionaryと呼ぶみたいですね）の型に対して、指定したキーに対応する値を返します。SwiftやRubyで言うところの`some_hash[key]`に相当する関数です。  
（具体例は長くなるので後ろに追記しました）

[code]

    lookup :: Ord k => k -> Map k a -> Maybe a 
    
[/code]

このとき、`lookup`の結果はキーに対応する値が入っていない可能性があるので、値が入っていないかも知れない（だから事前にチェックする必要がある）`Maybe
a`という文脈を持った値になります。  
一方、`lookup`の引数、つまりキーや検索する`Map`そのものは基本的に所与のものであるはずで、`lookup`がキーや`Map`自体の存在をチェックするのは、煩雑でしょう。それは`lookup`に引数を渡す側の責務です。

このようにして、引数と戻り値で文脈が異なる関数ができあがります。

# `IO`における「チグハグな関数」例

それから、すでにお使いであろう`putStrLn`関数も`String -> IO ()`という型なので、恐らく仰る「チグハグな関数」に該当するはずです。  
`IO`における「文脈」とは、「`IO`アクションを実行して、結果となる値を取得すること」に該当します。  
仮に`putStrLn`が`IO String -> IO
()`という、同じ文脈をそのまま使い回すような型であった場合、`putStrLn`が引数として受け取った`IO`アクションを実行しなければならなくなります。本来やりたいことは文字列を表示することなのに、余計な`IO`アクションを実行してしまうわけです。  
なので、`putStrLn`が欲しいのは単なる、`IO`という文脈を持たない`String`なのです。

※`IO`の例えはちょっとわかりづらいかも知れません。そもそも`IO`アクションってなんなの？ということが気になったら[IO
モナドと副作用](https://haskell.jp/blog/posts/2020/io-monad-and-
sideeffect.html)や、手前味噌で恐縮ですが[HaskellのIOは他の言語でいうところの関数オブジェクトとよく似てるよ、という話](https://the.igreque.info/posts/2017/01-haskell-
io-in-java.html)をご覧ください。

# そもそも「文脈」とは

ちなみに、Monadで言うところの「文脈」というのはここまで説明したとおり、

  * `Maybe`で言えば「値が入っているかどうか事前にチェックすること」
  * `IO`で言えば「値を取り出すために事前に実行すること」

といった具合に、「他の関数に値を渡す **前に** やらなければならない操作」と考えると、しっくりくるのではないかと思います。  
これは、具体的に言えば各種`Monad`のインスタンスにおける`>>=`の定義がそのまま相当しますし、`do`記法で言えば

[code]

    do
      result <- action1
      action2 result
      action3
    
[/code]

の、各`action`の「間」でやっていることに相当します。  
まさにアクションとアクションの「間」にあるものが「文脈」なのです。

# 追記: `>>=` の使用例

お待たせしております。ちょっと長くなってしまいましたが例を書きました。詳細はソースのコメントをご覧ください。

ここで使用している`Maybe`に対する`>>=`を`case`式に変えたり、`do`における`<-`を`>>=`に変えてみると、それぞれのありがたみがよくわかるのではないかと思います。  
（ **変え方についてわからないことがあればまた別に質問してください** ）  
また、解説が不十分だと感じたらコメントをください。

[code]

    import           Data.Maybe (listToMaybe)
    import           System.Environment (getArgs)
    import qualified Data.Map.Strict as M
    
    -- コマンドライン引数として受け取ったユーザー名から、
    -- ユーザーのお気に入りを取り出すプログラム
    
    -- | ユーザーの名前をキー、ユーザーのIDを値としたRDBのテーブルもどき
    userByName :: M.Map String Int
    userByName = M.fromList
      [ ("foo", 1)
      , ("bar", 2)
      , ("baz", 3)
      ]
    
    
    -- | ユーザーのIDをキー、ユーザーのお気に入りを値としたRDBのテーブルもどき
    favoritesById :: M.Map Int String
    favoritesById = M.fromList
      [ (1, "ice cream")
      , (3, "ramen")
      ]
    
    
    main :: IO ()
    main = do
      -- 一つ目の >>= を使っている例。
      -- getArgs :: IO [String] という型なので、コマンドライン引数を取り出すアクションを実行して
      -- 何でもない普通の[String]を取得する
      args <- getArgs
      let mFavorites :: Maybe String
          mFavorites = do
            -- 二つ目の >>= を使っている例
            -- listToMaybeでコマンドライン引数の先頭の要素を取り出して、
            -- Maybe Stringじゃない、ただのStringを取得する
            name <- listToMaybe args
            findFavoritesByName name
      print mFavorites
    
    
    -- | 三つ目の >>= を使っている例。
    --   RDBのJOINをするような要領で、
    --   ユーザーの名前からユーザーのお気に入りを取得する
    --   どちらかのテーブルに対応する値がなければ
    --   直ちにNothingが返る
    findFavoritesByName :: String -> Maybe String
    findFavoritesByName name = do
      userId <- M.lookup name userByName
      M.lookup userId favoritesById
    
[/code]




> 一方 Monad を使うのであれば、同じように +1 して返す関数は下記になると考えます。
[code]

>     incWrapedJust x = Just(x + 1)
>  
[/code]

この例があまり現実的ではないので、確かにこのような関数は定義しないでしょう。

# `Maybe`における「チグハグな関数」例

実際のところ、仰るような「チグハグな関数」は現実世界にはたくさんあります。  
典型的なのは[containersパッケージの`Data.Map.Strict`モジュール、`lookup`関数](http://hackage.haskell.org/package/containers-0.6.3.1/docs/Data-
Map-Strict.html#v:lookup)です。

この関数は、`Map`型という連想配列（RubyでHashと呼ばれるものに相当します。SwiftではDictionaryと呼ぶみたいですね）の型に対して、指定したキーに対応する値を返します。SwiftやRubyで言うところの`some_hash[key]`に相当する関数です。  
（具体例は長くなるので後ろに追記しました）

[code]

    lookup :: Ord k => k -> Map k a -> Maybe a 
    
[/code]

このとき、`lookup`の結果はキーに対応する値が入っていない可能性があるので、値が入っていないかも知れない（だから事前にチェックする必要がある）`Maybe
a`という文脈を持った値になります。  
一方、`lookup`の引数、つまりキーや検索する`Map`そのものは基本的に所与のものであるはずで、`lookup`がキーや`Map`自体の存在をチェックするのは、煩雑でしょう。それは`lookup`に引数を渡す側の責務です。

このようにして、引数と戻り値で文脈が異なる関数ができあがります。

# `IO`における「チグハグな関数」例

それから、すでにお使いであろう`putStrLn`関数も`String -> IO ()`という型なので、恐らく仰る「チグハグな関数」に該当するはずです。  
`IO`における「文脈」とは、「`IO`アクションを実行して、結果となる値を取得すること」に該当します。  
仮に`putStrLn`が`IO String -> IO
()`という、同じ文脈をそのまま使い回すような型であった場合、`putStrLn`が引数として受け取った`IO`アクションを実行しなければならなくなります。本来やりたいことは文字列を表示することなのに、余計な`IO`アクションを実行してしまうわけです。  
なので、`putStrLn`が欲しいのは単なる、`IO`という文脈を持たない`String`なのです。

※`IO`の例えはちょっとわかりづらいかも知れません。そもそも`IO`アクションってなんなの？ということが気になったら[IO
モナドと副作用](https://haskell.jp/blog/posts/2020/io-monad-and-
sideeffect.html)や、手前味噌で恐縮ですが[HaskellのIOは他の言語でいうところの関数オブジェクトとよく似てるよ、という話](https://the.igreque.info/posts/2017/01-haskell-
io-in-java.html)をご覧ください。

# そもそも「文脈」とは

ちなみに、Monadで言うところの「文脈」というのはここまで説明したとおり、

  * `Maybe`で言えば「値が入っているかどうか事前にチェックすること」
  * `IO`で言えば「値を取り出すために事前に実行すること」

といった具合に、「他の関数に値を渡す **前に** やらなければならない操作」と考えると、しっくりくるのではないかと思います。  
これは、具体的に言えば各種`Monad`のインスタンスにおける`>>=`の定義がそのまま相当しますし、`do`記法で言えば

[code]

    do
      result <- action1
      action2 result
      action3
    
[/code]

の、各`action`の「間」でやっていることに相当します。  
まさにアクションとアクションの「間」にあるものが「文脈」なのです。

# 追記: `>>=` の使用例

お待たせしております。ちょっと長くなってしまいましたが例を書きました。詳細はソースのコメントをご覧ください。

ここで使用している`Maybe`に対する`>>=`を`case`式に変えたり、`do`における`<-`を`>>=`に変えてみると、それぞれのありがたみがよくわかるのではないかと思います。  
（ **変え方についてわからないことがあればまた別に質問してください** ）  
また、解説が不十分だと感じたらコメントをください。

[code]

    import           Data.Maybe (listToMaybe)
    import           System.Environment (getArgs)
    import qualified Data.Map.Strict as M
    
    -- コマンドライン引数として受け取ったユーザー名から、
    -- ユーザーのお気に入りを取り出すプログラム
    
    -- | ユーザーの名前をキー、ユーザーのIDを値としたRDBのテーブルもどき
    userByName :: M.Map String Int
    userByName = M.fromList
      [ ("foo", 1)
      , ("bar", 2)
      , ("baz", 3)
      ]
    
    
    -- | ユーザーのIDをキー、ユーザーのお気に入りを値としたRDBのテーブルもどき
    favoritesById :: M.Map Int String
    favoritesById = M.fromList
      [ (1, "ice cream")
      , (3, "ramen")
      ]
    
    
    main :: IO ()
    main = do
      -- 一つ目の >>= を使っている例。
      -- getArgs :: IO [String] という型なので、コマンドライン引数を取り出すアクションを実行して
      -- 何でもない普通の[String]を取得する
      args <- getArgs
      let mFavorites :: Maybe String
          mFavorites = do
            -- 二つ目の >>= を使っている例
            -- listToMaybeでコマンドライン引数の先頭の要素を取り出して、
            -- Maybe Stringじゃない、ただのStringを取得する
            name <- listToMaybe args
            findFavoritesByName name
      print mFavorites
    
    
    -- | 三つ目の >>= を使っている例。
    --   RDBのJOINをするような要領で、
    --   ユーザーの名前からユーザーのお気に入りを取得する
    --   どちらかのテーブルに対応する値がなければ
    --   直ちにNothingが返る
    findFavoritesByName :: String -> Maybe String
    findFavoritesByName name = do
      userId <- M.lookup name userByName
      M.lookup userId favoritesById
    
[/code]

