の重要な機能は、タイプMonadの「内部を見て」 、 ;を確認することです。ただし、の重要な制限は、モナドが「不可避」である可能性がなければならないことです。つまり、型クラス操作は、型の関数を記述するのに十分であってはなりません。 まさにこの能力をあなたに与えます。m aaMonadMonadMonad m => m a -> a(>>=) :: Monad m => m a -> (a -> m b) -> m b
しかし、それを達成する方法は複数あります。Monadクラスは次のように定義できます。
class Functor f where
fmap :: (a -> b) -> f a -> f b
class Functor f => Monad m where
return :: a -> m a
join :: m (m a) -> m a
なぜ私たちが機能を持てなかったのかとあなたは尋ねMonad m => m a -> (m a -> m b) -> m bます。まあ、与えられたf :: a -> b、fmap f :: ma -> mb基本的にそれです。しかしfmap、それだけでは「中を見る」Monad m => m aことはできませんが、そこから逃れることはできません。しかしjoin、fmap一緒にあなたにその能力を与えます。 とで一般的に(>>=)書くことができます:fmapjoin
(>>=) :: Monad m => m a -> (a -> m b) -> m b
ma >>= f = join (fmap f ma)
実際、これは、の定義を思い付くのにMonad問題があるときにインスタンスを定義するための一般的なトリックです—モナドになる関数を記述してから、の一般的な定義を使用します。(>>=)join(>>=)
ええと、それは「いいえ」で質問の「それがそうである必要があるか」の部分に答えます。しかし、なぜそれがそうであるのですか?
Haskellの設計者について話すことはできませんが、私はそれを次のように考えるのが好きです。Haskellのモナディックプログラミングでは、基本的な構成要素は次のようなアクションです。
getLine :: IO String
putStrLn :: String -> IO ()
より一般的には、これらの基本的な構成要素には、、、、、 ...、のMonad m => m aようMonad m => a -> m bなタイプがあります。人々は非公式にこれらの行動を呼びます。 は引数なしのアクション、は1つの引数のアクションなどです。Monad m => a -> b -> m cMonad m => a -> b -> ... -> m zMonad m => m aMonad m => a -> m b
さて、(>>=) :: Monad m => m a -> (a -> m b) -> m b基本的には2つのアクションを「接続」する最も単純な関数です。 getLine >>= putStrLnは、最初に実行getLineし、次に実行putStrLnして、実行から得られた結果を渡すアクションですgetLine。あなたが持っていてfmap、持っていjoinなかった場合、あなたはこれを書かなければならない>>=でしょう:
join (fmap putStrLn getLine)
さらに一般的には(>>=)、アクションの「パイプライン」のような概念を具体化するため、一種のプログラミング言語としてモナドを使用するためのより便利な演算子です。
Control.Monad最後に、モジュールを認識していることを確認してください。returnと(>>=)はモナドの基本関数ですが、これら2つを使用して定義できる高レベルの関数は他にも無限にあり、そのモジュールはより一般的な関数を数十個収集します。(>>=)あなたのコードは;によって拘束衣に強制されるべきではありません。これは、それ自体で、およびより大きなビルディングブロックのコンポーネントとして役立つ重要なビルディングブロックです。