fmap和bind之间的能力差异?

m0m*_*eni 12 monads haskell functional-programming functor

我是函数式编程的新手(来自javascript),我很难分辨两者之间的区别,这也搞砸了我对functor与monads的理解.

仿函数:

class Functor f where
    fmap :: (a -> b) -> f a -> f b
Run Code Online (Sandbox Code Playgroud)

Monad(简化):

class Monad m where
    (>>=) :: m a -> (a -> m b) -> m b
Run Code Online (Sandbox Code Playgroud)
  • fmap 接受函数和仿函数,并返回一个仿函数.
  • >>= 接受一个函数和一个monad,并返回一个monad.

两者之间的区别在于函数参数:

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

>>=获取一个返回monad的函数参数.我知道这很重要,但是我很难看到这个轻微的东西如何使monad比functor强大得多.谁能解释一下?

Mat*_*hid 14

好吧,(<$>)是一个别名fmap,与交换的参数(=<<)相同(>>=):

(<$>) :: (x ->   y) -> b x -> b y
(=<<) :: (x -> b y) -> b x -> b y
Run Code Online (Sandbox Code Playgroud)

现在相当明显:使用bind函数,我们应用一个返回a b y而不是a 的函数y.那会有什么不同呢?

考虑这个小例子:

foo <$> Just 3
Run Code Online (Sandbox Code Playgroud)

请注意,这(<$>)将适用foo3,并将结果放回到Just.换句话说,这个计算的结果不可能Nothing.反之:

bar =<< Just 3
Run Code Online (Sandbox Code Playgroud)

这个计算可以返回Nothing.(例如,bar x = Nothing会这样做.)

我们可以用list monad做类似的事情:

foo <$> [Red, Yellow, Blue]   -- Result is guaranteed to be a 3-element list.
bar =<< [Red, Yellow, Blue]   -- Result can be ANY size.
Run Code Online (Sandbox Code Playgroud)

简而言之,对于(<$>)(即fmap),结果的"结构"总是与输入相同.但是对于(=<<)(即(>>=)),结果的结构可能会改变.这允许条件执行,对输入做出反应,以及一大堆其他事情.

  • 为了完整性,Applicative也可以返回`Nothing`:`Nothing <*>只需3`.不同之处在于,"管道"(即计算结构)在组成计算时*固定*,*在*之前*"运行"*.但是对于Monads来说,管道可以根据产生的值而改变*而*它"运行".(在IO的情况下,可能会接收到"3"作为用户的输入). - *list*示例是esp.好在这里:`(foo <$>)`保持结构(列表的长度); `([baz,quux] <*>)`将改变结构*可预测*(创建长度为6的列表); 所有的投注都是关闭的. (4认同)

HEG*_*X64 9

简短的回答是,如果你能以一种有意义的方式m (m a)转变m a,那么它就是一个Monad.这对于所有Monad都是可能的,但不一定适用于Functors.

我认为最令人困惑的事情是,所有的函子的常见的例子(例如List,Maybe,IO)也单子.我们需要一个像Functor但不是Monad的例子.

我将使用假设日历程序中的示例.以下代码定义了一个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)
Run Code Online (Sandbox Code Playgroud)

Event对象存储事件发生的时间和一些可以使用的更改数据fmap.现在让我们试着让它成为Monad:

instance Monad Event where
    (>>=) (MkEvent timeA a) f = let (MkEvent timeB b) = f a in
                                MkEvent <notSureWhatToPutHere> b
Run Code Online (Sandbox Code Playgroud)

我们发现我们不能,因为你最终会得到两个LocalTime对象.timeA从给定的Event,并timeBEvent通过的结果给出f a.我们的Event类型被定义为只有一个LocalTime(time)它出现在,所以如果不将两个LocalTimes变成一个Monad是不可能的.(在某些情况下,这样做可能有意义,所以如果你真的想这么做就可以把它变成monad).

  • 不是monad的经典/共同仿函数的一个例子是`newtype Const ab = Const a`. (3认同)
  • monad定律要求`pure x >> = f`为`fx`,但`pure :: b - > Const ab`不可能使用它的参数. (3认同)
  • @ HEGX64:对.但是对于`Functor`这没有问题 - 事实上,它立即保证了仿函数法`fmapid≡id`. (3认同)