40

私はGrand Haskell Crusade (GHC :) )を始めましたが、モナドとIO関数について少し混乱しています。これら2つの機能の違いを簡単に説明できる人はいますか?

f1 = do x <- [1,2]
        [x, x+1] -- this is monad, right?

f2 = do x <- [1,2]
        return [x, x+1]

結果は次のとおりです。

*Main> f1
[1,2,2,3]

*Main> f2
[[1,2],[2,3]]
4

6 に答える 6

45

なぜあなたが生じる特定の答えを得るのかを見るために、脱糖の説明は非常に役に立ちます。Haskellコードの認識を発展させることについての少し一般的なアドバイスでそれらを補足させてください。

Haskellの型システムは、2つの分離可能な「道徳的」目的を区別しません。

  • [x]から抽出された要素を含むリストであるのタイプx
  • [x]優先選択を可能にする要素の計算のタイプx

これらの2つの概念が同じ表現を持っているという事実は、それらが同じ役割を果たしていることを意味するものではありません。でf1[x, x+1]は計算の役割を果たしているので、それが生成する可能性は、計算全体によって生成される選択肢にマージされます。これ>>=が、リストモナドの機能です。f2ただし、では、が[x, x+1]値の役割を果たしているため、計算全体で2つの値(たまたまリスト値)から優先順位が付けられた選択肢が生成されます。

Haskellはこの区別をするために型を使用していません[そしてあなたは今までにそうすべきだと思うかもしれませんが、それは別の話です]。代わりに、構文を使用します。したがって、コードを読むときに価値と計算の役割を認識するように頭を訓練する必要があります。表記は、計算を構築するためのdo特別な構文です。内部にあるものは、次のテンプレートキットから構築されています。do

計算用のジグソーピース

3つの青い部分が計算をdo行います。計算の穴を青で、値の穴を赤でマークしました。これは完全な構文を意味するものではなく、頭の中でコードの断片を認識する方法のガイドにすぎません。

実際、適切なモナディックタイプであれば、青い場所に古い式を記述できます。そのように生成された計算は、必要に応じて使用して全体の計算にマージさ>>=れます。あなたのf1例では、あなたのリストは青い場所にあり、優先順位の高い選択肢として扱われます。

同様に、モナディックタイプ(この場合はリストなど)が非常によくある赤い場所に式を書くこともできますが、それらはすべて同じ値として扱われます。それがで起こることですf2:いわば、結果の外側の括弧は青ですが、内側の括弧は赤です。

コードを読むときに値と計算を分離するように脳を訓練して、テキストのどの部分がどの仕事をしているのかを本能的に知ることができるようにします。f1頭を再プログラムすると、との区別f2は完全に正常に見えます!

于 2012-07-04T09:46:40.793 に答える
30

ここでの他の答えは正しいですが、それらがあなたが必要としているものではないのではないかと思います...これをできるだけシンプルに保つようにします.2つのポイントだけです:


ポイント 1.returnはHaskell 言語では特別なことではありません。これはキーワードではありませんし、他の何かの構文糖衣でもありません。これは、型クラスの一部である単なる関数ですMonad。その署名は単純です:

return :: a -> m a

mその時点で話しているモナドはどこにありますか。「純粋な」値を取り、それをモナドに詰め込みます。pure(ちなみに、基本的には の同義語であると呼ばれる別の関数がありreturnます... 名前がより明白なので、私はそれが好きですm! return)

return :: a -> [a]

それが役立つ場合は、型 synonymtype List a = [a]を考えてListみてくださいm。とにかく、return自分で実装しようとしている場合、それを実装する唯一の合理的な方法は、何らかの値 (タイプaに関係なく) を取得し、それ自体をリストに貼り付けることです。

return a = [a]

だから私はreturn 1リストモナドで言うことができ、私は得るでしょう[1]. 私も同様に言うことができreturn [1, 2, 3]、私は得るでしょう[[1, 2, 3]]


ポイント 2.IOはモナドですが、すべてのモナドが というわけではありませんIO多くの Haskell チュートリアルでは、主に歴史的な理由で 2 つのトピックを混同しているようです (ちなみに、return名前があまりよくないことにつながったのと同じ紛らわしい歴史的な理由です)。それについて(理解できる)混乱があるように思えます。

あなたのコードでは、あなたが書いたのでリストモナドdo x <- [1, 2]にいます。たとえば、代わりに書いdo x <- getLineた場合、IOモナドにいるでしょう(getLinereturnsのためIO String)。とにかく、あなたはリストモナドにいるので、return上記のリストの定義を取得します。また、次のように定義され>>=た (の反転バージョン) であるのリストの定義も取得します。concatMap

concatMap :: (a -> [b]) -> [a] -> [b]
concatMap f xs = concat (map f xs)

他の投稿された回答は、ここからほとんどカバーされています:)私はあなたの質問に直接答えていないことを知っていますが、代わりにこれらの2つのポイントが、あなたが混乱していると思うかもしれない基本的なことに対処することを願っています.

于 2012-07-05T04:25:32.287 に答える
24

bindreturnを使用してコードを書き直すと、簡単に確認できます。

[1,2] >>= (\x->        [x,x+1]) === concatMap (\x-> [  x,x+1  ]) [1,2]

[1,2] >>= (\x-> return [x,x+1]) === concatMap (\x-> [ [x,x+1] ]) [1,2]

最初のコードはjoin、2 番目の結果を呼び出し、 によって導入された 1 つのモナドの「レイヤー」を削除し、使用中のモナドのreturn :: a -> m aリストの「リスト」と混同することに等しいです。たとえば、ペアを返す場合、 を省略してもあまり意味がありませんreturn

                                     -- WRONG: type mismatch
[1,2] >>= (\x->        (x,x+1)) === concatMap (\x-> (  x,x+1  )) [1,2]
                                     -- OK:
[1,2] >>= (\x-> return (x,x+1)) === concatMap (\x-> [ (x,x+1) ]) [1,2]

または、join/fmap次のように書き直すこともできます。

ma >>= famb === join (fmap famb ma)   -- famb :: a -> m b, m ~ []

join (fmap (\x->        [x,x+1]) [1,2]) = concat [ [  x,x+1  ] | x<-[1,2]]
join (fmap (\x->        (x,x+1)) [1,2]) = concat [ (  x,x+1  ) | x<-[1,2]]  -- WRONG
join (fmap (\x-> return [x,x+1]) [1,2]) = concat [ [ [x,x+1] ] | x<-[1,2]]

                                                         =  [y | x<-[1,2], y<-[ x,x+1 ]]
                                            {- WRONG -}  =  [y | x<-[1,2], y<-( x,x+1 )]
                                                         =  [y | x<-[1,2], y<-[[x,x+1]]]
于 2012-07-04T06:28:49.300 に答える
14

リスト型 ( []) はモナドです、はい。

さて、何をするか覚えておいてreturnください。これは、その型シグネチャから簡単に確認できます: return :: Monad m => a -> m a. のリスト型を置き換えましょう: return :: a -> [a]. したがって、この関数は何らかの値を取り、その値のリストだけを返します。に相当し\ x -> [x]ます。

したがって、最初のコード サンプルでは、​​最後に 1 つのリストがあります: [x, x+1]. 2 番目のサンプルでは、​​ネストされたリストがあります。1 つのリストは から取得され[x, x + 1]別のリストは から取得されreturnます。この場合、行return [x, x + 1]を書き換えることができます[[x, x + 1]]

最後に、結果は可能なすべての結果のリストです。つまり、as の結果と as の結果を連結しますx(行のおかげで)。したがって、最初のケースでは、2 つのリストを連結します。2 番目のケースでは、リストの2 つのリストを連結します。1x2x <- [1,2]return

于 2012-07-04T06:30:26.153 に答える
8

do構文を同等のものに脱糖する

f1 = [1,2] >>= \x -> [x, x+1]
f2 = [1,2] >>= \x -> return [x, x+1]

さて、クラス>>=から来て、Monad

class Monad m where
    (>>=) :: m a -> (a -> m b) -> m b
    return :: a -> m a

>>=と の両方f1のLHSf2[a](aデフォルトで に設定されているInteger) であるため、実際に検討しています

instance Monad [] where
    (>>=) :: [a] -> (a -> [b]) -> [b]
    ...

これは、と同じモナド則に従いますが、異なるモナドです。

instance Monad IO where ...

そして>>=他のモナドについても、あなたが知っていることを盲目的に他のモナドに当てはめないでください。:)

instance Monad []GHCではこのように定義されています

instance Monad [] where
    m >>= k = foldr ((++) . k) [] m
    return x = [x]
    ...

しかし、おそらく理解しやすい[]>>=

instance Monad [] where
    m >>= k = concatMap k m

これをオリジナルに適用すると、

f1 = concatMap (\x -> [x, x+1]) [1,2]
f2 = concatMap (\x -> [[x, x+1]]) [1,2]

と の値が何であるかは明らかf1ですf2

于 2012-07-04T06:30:48.027 に答える
1

私の理解では、

List モナドが do と同じ場合は [1,2] を返す

func :: Maybe (Maybe Int)
func = return $ Just 1

[] は単なるシンタックス シュガーなので、ラップされたリストになってしまうのはそのためです。

彼が本当にやりたいと思ったとき

func :: Maybe Int
func = return 5

また

func = Just 5

Maybe モナドで何が起こっているのかを理解するのは少し簡単だと思います。

だからあなたがするとき

return [1,2]

あなたは同じことをしています

[ [1,2] ]
于 2012-07-05T15:31:13.730 に答える