-1

重複の可能性:
モナドとは?

私は Haskell の関数型言語でプログラミングすることを学んでおり、パーサーを勉強しているときにモナドに出くわしました。私はそれらについて聞いたことがなかったので、それらが何であるかを知るために追加の研究を行いました.

このトピックを学ぶためにどこを見ても、私は混乱しています。モナドとは何か、モナドをどのように使用するかについての簡単な定義を実際に見つけることができません。「モナドは、値とそれらの値を使用した一連の計算の観点から計算を構造化する方法です」-ええ???

Haskell におけるモナドとは何か、モナドに関連する法則の簡単な定義と例を挙げてください。

  • 注: I/O アクションと副作用のある関数を見てきたので、do 構文の使用方法を知っています。
4

3 に答える 3

6

直感

大まかな直感では、モナドは特定の種類のコンテナー(Functor)であり、2つの操作を使用できます。return単一の要素をコンテナに取り込むラッピング操作。joinコンテナーのコンテナーを単一のコンテナーにマージする操作。

return :: Monad m => a -> m a     
join   :: Monad m => m (m a) -> m a

したがって、モナドの場合、多分あなたは持っています:

return :: a -> Maybe a     
return x = Just x

join :: Maybe (Maybe a) -> Maybe a
join (Just (Just x) = Just x
join (Just Nothing) = Nothing 
join Nothing        = Nothing

同様に、モナド[]の場合、これらの操作は次のように定義されます。

return :: a -> [a]     
return x = [x]

join :: [[a]] -> [a]
join xs = concat xs

モナドの標準的な数学的定義は、これらの戻り演算子と結合演算子に基づいています。ただし、Haskellでは、クラスMonadの定義により、結合の代わりにバインド演算子が使用されます。

Haskellのモナド

関数型プログラミング言語では、これらの特別なコンテナーは通常、効果的な計算を示すために使用されます。タイプMaybe aは、成功する場合と成功しない場合がある計算を表し、タイプ[a]は非決定論的な計算を表します。a->m b特に、効果のある関数、つまりいくつかの型を持つ関数に関心がありますMonad m。そして、それらを構成できる必要があります。これは、モナディックコンポジションまたはバインド演算子のいずれかを使用して実行できます。

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

Haskellでは後者が標準的なものです。その型はアプリケーション演算子の型と非常に似ていることに注意してください(ただし、引数が反転しています)。

(>>=)    :: Monad m => m a -> (a -> m b) -> m b
flip ($) ::              a -> (a ->   b) -> b

効果的な関数とタイプの値を返すf :: a -> m b計算を受け取り、アプリケーションを実行します。では、モナドでこれをどのように行うのでしょうか?コンテナ()はマッピングできます。この場合、結果は計算内の計算になり、それをフラット化できます。mx :: m aamx >>= fFunctors

fmap f mx        :: m (m b)
join (fmap f mx) :: m b

だから私たちは持っています:

(mx >>= f) = join (fmap f mx) :: m b

これが実際に機能することを確認するには、リスト(非決定論的関数)を使用した簡単な例を検討してください。可能な結果のリストと非決定mx = [1,2,3]論的関数があるとしますf x = [x-1, x*2]。計算mx >>= fするには、まずmxをfにマッピングしてから、結果をマージします。

fmap f mx                = [[0,2],[1,4],[2,6]]    
join [[0,2],[1,4],[2,6]] = [0,2,1,4,2,6]

Haskellではバインド演算子(>>=)がより重要でjoinあるため、後者の効率上の理由から前者から定義され、その逆ではありません。

join mx = mx >>= id

また、joinおよびfmapで定義されているバインド演算子を使用して、マッピング操作を定義することもできます。このため、モナドはクラスFunctorのインスタンスである必要はありません。fmapと同等の操作liftMは、Monadライブラリで呼び出されます。

liftM f mx = mx >>= \x-> return (f x) 

したがって、モナドの実際の定義は次のようになります。

return :: a -> Maybe a     
return x = Just x

(>>=)    :: Maybe a -> (a -> Maybe b) -> Maybe b
Nothing >>= f = Nothing 
Just x  >>= f = f x

そしてモナドの場合[]:

return :: a -> [a]     
return x = [x]

(>>=)    :: [a] -> (a -> [b]) -> [b]
xs >>= f = concat (map f xs)
         = concatMap f xs    -- same as above but more efficient

独自のモナドを設計する場合、直接定義するのではなく(>>=)、問題を部分に分割して、構造をマッピングおよび結合する方法を理解する方が簡単な場合があります。マップと結合があると、必要な法則を満たしているという意味で、モナドが明確に定義されていることを確認するのにも役立ちます。

モナド法

モナドはファンクターである必要があるため、マッピング操作は次の条件を満たす必要があります。

fmap id = id
fmap g . fmap f = fmap (g . f)

復帰および参加の法律は次のとおりです。

join . return      = id
join . fmap return = id  
join . join = join . fmap join 

最初の2つの法則は、マージによってラッピングが元に戻されることを指定しています。コンテナを別のコンテナでラップすると、joinによって元のコンテナが返されます。ラッピング操作を使用してコンテナーの内容をマップすると、再度結合すると、最初に持っていたものが返されます。最後の法則は、結合の結合法則です。コンテナが3層ある場合は、内側または外側からマージしても同じ結果が得られます。

ここでも、joinとfmapの代わりにbindを使用できます。取得する法則は少なくなりますが、(おそらく)より複雑になります。

return a >>= f  = f a
m >>= return    = m
(m >>= f) >>= g = m >>= (\x -> f x >>= g) 
于 2012-08-30T15:28:31.283 に答える
4

Haskellのモナドは、次の2つの操作が定義されているものです。

(>>=)  :: Monad m => m a -> (a -> m b) -> m b -- also called bind
return :: Monad m => a -> m a

これらの2つの操作は、マシーなアイデアのコツがない場合、この時点で本当に混乱する可能性のある特定の法則を満たす必要があります。概念的には、bindを使用してモナディックレベルの値を操作し、「トリビアル」な値からモナディック値を作成するために戻ります。例えば、

getLine :: IO String

putStrLnしたがって、これを変更することはできませんString-これStringIO String!ではないためです。

さて、IOモナドが便利なので心配しないでください。私たちがしなければならないのは、バインドを使用して必要なことを実行することだけです。IOモナドでバインドがどのように見えるかを見てみましょう。

(>>=) :: IO a -> (a -> IO b) -> IO b

そしてgetLine、バインドの左側に配置すると、さらに具体的にすることができます。

(>>=) :: IO String -> (String -> IO b) -> IO b

さて、getLine >>= putStrLn . (++ ". No problem after all!")追加のコンテンツを追加して、入力した行を印刷します。右側は、を取り、String生成する関数ですIO ()-まったく難しくありませんでした!タイプだけで行きます。

Maybeたとえば、など、さまざまなタイプに対して定義されたモナドがあり[a]、それらは概念的に同じように動作します。

Just 2 >>= return . (+2)Just 4あなたが期待するかもしれないように、をもたらすでしょう。ここで使用する必要があることに注意してくださいreturn。そうしないと、右側の関数が戻り型と一致せm b、単にb、型エラーになるためです。putStrLnの場合は、すでにIO何かを生成しているため、これは機能しました。これは、まさに私たちのタイプが一致する必要があるものでした。(ネタバレ:すべてがであるfoo >>= return . barため、形の表現はばかげています。それが何を意味するのか理解できますか?)MonadFunctor

私は個人的に、これは直感がモナドのトピックにあなたを導く限りであると思います、そしてあなたがもっと深く掘り下げたいのなら、あなたは本当に理論に飛び込む必要があります。私はそれらを最初に使うだけのコツをつかむのが好きでした。さまざまなMonadインスタンスのソースを検索できます。たとえば、List([])MonadまたはHoogleのMaybeMonadを検索して、正確な実装を少し賢くすることができます。それに慣れたら、実際のモナド法を試して、それらについてより理論的な理解を得てみてください!

于 2012-08-29T17:10:54.463 に答える
2

Typeclassopediaにはについてのセクションがあります(ただし、最初Monadにについての前のセクションを読んでください)。FunctorApplicative

于 2012-08-30T08:48:34.513 に答える