IO 是 Functor 的一个实例仅仅是因为它首先是一个 Monad 吗?

Is IO an instance of Functor just because it is a Monad in the first place?

来自LYAH I understand that the do notation is just syntactic sugar for monadic style; and from the wikibook 我读得差不多;所以我的理解是,如果没有 Monad 实例,就不会有任何 do 符号。

然而我读到了 IO 类型构造函数的 Functor 实例的定义。

instance Functor IO where  
    fmap f action = do  
        result <- action  
        return (f result)  

这只是下面的语法糖,不是吗?

instance Functor IO where  
    fmap f action = action >>= return . f  

这意味着基础假设 IO 首先是 Monad 的实例;这与每个 Monad 都是 Functor 而不是相反的事实相悖。

事实上,我已经理解 MonadApplicative 更“重要”,而 Applicative 又比 Functor 更重要Applicative 的定义对其实例强制执行 Functor 约束(并且 Monad 的定义理想地要求其实例是 Applicative,如 如果它不是 Applicative).

,请不要将其设为 Monad

换句话说,上面的代码让我觉得如果 IO 不是 Monad,就没有办法为 IO 编写 Functor 实例] 排在首位。

现在我想起来了,也许这就像说 IO 是作为成熟的 Monad 创建的,上面的实例后来只是为了完整性和数学一致性。

但是我很困惑,所以我在这里寻求帮助。

具有 Monad 实例的类型意味着它必须具有 Functor(和 Applicative)的定义,但这并不意味着 Functor 实例必须定义为“第一”,只是两个实例都必须存在。只要方法实现不是循环定义的,就没有问题。

事实上,通常先为一个类型实现Monad,然后根据Monad操作机械地定义ApplicativeFunctor,因为Monad更强大,所以其他实例只是限制Monad实例:

-- These work for any T with a Monad instance.

instance Functor T where
  fmap f x = do
    x' <- x
    return (f x')

instance Applicative T where
  pure = return
  f <*> x = do
    f' <- f
    x' <- x
    return (f' x')

这恰恰是因为“a Monad ‘不止于’ an Applicative,而后者又‘不止于’ a Functor".

还值得注意的是,最初 MonadFunctor class 是不相关的, Applicative class (后来添加的)是以及。每个函数都有单独的等效函数,例如 fmapliftAliftMpurereturn(<*>)aptraversemapM。按照惯例,您会编写一个 Monad 实例并据此实现其他 classes。这些单独的定义仍然存在,但现在是多余的:因为“Applicative–Monad Proposal”使 Applicative 成为 Monad 的超级 class 和 [=13= 的 Functor ],您可以始终使用例如pure 而不是 returntraverse 而不是 mapM,因为它们是相同的函数,但在更严格的上下文中工作。因此,还有一些历史背景可以说明为什么一个类型的这些实例可能有单独的定义。

正如@dfeuer 指出的那样,有些数据结构 mapM (mapM_, forM, forM_) 比 traverse 更有效(分别为 traverse_forfor_),例如 Data.Vector 类型。在向量的特定情况下,我相信这是因为库可以利用单子排序作为优化,以实现更多的流式传输和就地分配结果。但它们是等价的,因为 traverse 应该总是产生相同的结果。