2016-02-14 9 views
8

私は関数型プログラミング(JavaScriptから来て)には新しく、2つの違いを伝えるのは苦労しています。これは、ファンクターとモナドの理解にも困惑しています。fmapとbindの機能の違いは?

ファンクタ:

class Functor f where 
    fmap :: (a -> b) -> f a -> f b 

モナド(簡体字):

class Monad m where 
    (>>=) :: m a -> (a -> m b) -> m b 
    fmap
  • 機能とファンクタをとり、ファンクタを返します。
  • >>=は、関数とモナドをとり、モナドを返します。

2つの違いは、関数のパラメータである:

    fmap
  • - (a -> b)
  • >>=から(a -> m b)

>>=はモナドを返す関数のパラメータを取ります。私はこれが重要であることを知っていますが、この1つの小さなことがどのようにしてモナドをどのようにしてファンクタよりもずっと強力にするのかを見るのが難しいです。誰か説明できますか?

+4

これは、反転されたバージョンの '(>> =)'、['(= <<)'](https://stackoverflow.com/questions/34545818/is-monad-bind-operator- function-composition-chaining-fun-fun/34561605#34561605)を使用して作成することができます。 '(g <$>):: f a - > f b'では、' g :: a - > b'関数は 'f'" wrapping "に影響を与えません。 '(k = <<) :: m a -> mb'で、関数' k :: a - > mb'それ自体は新しい 'm''ラッピングを作成するので変更可能です。 –

+0

@WillNessこれを"理解する "ことはできますが、実際の問題は、 '' fmap'はできないことを '' = ''できないことが分かりません。私の頭の中では、私が見たことがないので同等ですfmapが不十分であることを示す例です。 – m0meni

+4

リストを使って、リストからいくつかの要素を 'map'を使ってフィルタリングしようとしますが、' concatMap'を使うと 'map(\ x- > x + 1)[1,2,3] 'vs' concatMap(\ x-> [x、x + 1 |偶数x])[1,2,3]) 'となります。 –

答えて

13

まあ、(<$>)fmapの別名である、とが入れ替わった引数で(>>=)と同じです:

(<$>) :: (x -> y) -> b x -> b y 
(=<<) :: (x -> b y) -> b x -> b y 

差は今かなり明確である:バインド機能で、我々は返す関数を適用yではなくb yです。だから違いは何ですか? 3fooを適用し、Justに戻した結果を置く

foo <$> Just 3 

お知らせ(<$>)こと:

は、この小さな例を考えてみましょう。換言すれば、この計算の結果は であり、Nothingではありません。それどころか:

bar =<< Just 3 

この計算Nothingを返すことができます。 (例えば、bar x = Nothingはそれを行います。)

我々はリストモナドと同様のことを行うことができます。結果の(<$>)(すなわち、fmap)、「構造」と、一言で言えば

foo <$> [Red, Yellow, Blue] -- Result is guaranteed to be a 3-element list. 
bar =<< [Red, Yellow, Blue] -- Result can be ANY size. 

入力と常に同じです。しかし、(すなわち、(>>=))の場合、結果の構造が変わる可能性があります。条件付き実行、入力への反応、および他のものの全体の束を許可します。

+4

完全性のために、アプリケーションは 'Nothing'も返すことができます:' Nothing <*> Just 3'。違いは、計算が構成されているときには、「配管」(すなわち計算構造)が*固定*されていることである。しかし、Monadsでは、実行中の値に応じて配管が変わることがあります。 (IOの場合は、例えば、ユーザの入力として「3」が受信されたと考えられる)。 - * list *の例はespです。ここで良い: '(foo <$>)'は構造体(リストの長さ)を保持します。 '([baz、quux] <*>)'は構造体を予測可能に変更する*(長さ6のリストを作成する);モナドとのすべての賭けはオフです。 –

8

短い答えはm (m a)m aに変換することができればそれはモナドです。これはすべてのモナドで可能ですが、必ずしもFunctorsではありません。

Functorsの一般的な例(例:ListMaybeIO)はすべてもっとも混乱しやすいと思います。ファンクターではなくモナドではないものの例が必要です。

私は仮説的なカレンダープログラムの例を使用します。次のコードでは、イベントと発生するデータを格納するFunctor(Event Functor)を定義しています。

import Data.Time.LocalTime 

data Event a = MkEvent LocalTime a 

instance Functor Event where 
    fmap f (MkEvent time a) = MkEvent time (f a) 
Event

オブジェクトストアイベントが発生した時間とfmapを使用して変更することができるいくつかの追加データ。我々は、我々はあなたが2つのLocalTimeオブジェクトで終わるだろうことはできないので、ことがわかり

instance Monad Event where 
    (>>=) (MkEvent timeA a) f = let (MkEvent timeB b) = f a in 
           MkEvent <notSureWhatToPutHere> b 

を:今すぐモナドを試してみて、それを作ることができます。 timeAからEventtimeBとなり、Eventの結果は、f aとなります。私たちのEventタイプは、それが発生するので、Monadを作ることはできませんLocalTimetime)の1つだけを持つように定義されています2つのLocalTimeを1に変換せずに。 (そうすることが意味をなさない場合もありますので、本当にしたい場合はこれをモナドにすることができます)。

+3

モナドではない古典的/一般的なファンクタの一例は 'newtype Const a b = Const a'です。 – dfeuer

+3

'pure x >> = f'はモナドの法則によって' f x'になる必要がありますが、 'pure :: b - > Const a b'はおそらくその引数を使うことができません。 – dfeuer

+1

@dfeuerこのシーム(シンプルすぎる)(https://ncatlab.org/nlab/show/too+simple+to+be+simple)。また、私はfmap f(Const x)= Const x以外のFunctorインスタンスを書く方法を見つけることができません – HEGX64

3

IOはちょうどFunctorであり、Monadではないとします。どのようにして2つの行動を順序付けることができますたとえば、getChar :: IO CharputChar :: Char -> IO()のように言います。

putCharを使用してgetChar(実行時にはCharをstdinから読み取るアクション)を試すことができます。

fmap putChar getChar :: IO (IO()) 

今、私たちが実行されると、標準入力からCharを読み取り、実行、プログラムを作成し、プログラムを持って、stdoutにCharを書き込みます。しかし、私たちが実際に望むのは、実行するとstdinからCharを読み込み、Charをstdoutに書き込むプログラムです。それ自体で

join :: IO (IO()) -> IO() 

Functorがこの機能を提供していません。だから我々は(IO場合は、「シーケンシング」)「フラット化」型を持つ関数が必要です。しかし、それはより一般的な型を持つMonadの関数である:

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

何このすべては>>=と関係があるのでしょうか?偶然にも、単項バインドはfmapjoinだけの組み合わせです:

:t \m f -> join (fmap f m) 
(Monad m) => m a1 -> (a1 -> m a) -> m a 

違いを見てのもう一つの方法は、fmapが決してマッピングされた値の全体的な構造を変えず、join(したがって>>=同様)ということですそれを行うことができます。

IOアクションでは、fmapとなります。は、addicionalの読み書きやその他の影響を引き起こします。しかし、joinは、内部動作の読み出し/書き込みを外部動作の読み出し/書き込みに引き継ぐ。

関連する問題