10

在“向您学习 Haskell for Great Good!” 作者声称Applicative IO实例是这样实现的:

instance Applicative IO where
    pure = return
    a <*> b = do
        f <- a
        x <- b
        return (f x)

我可能是错的,但似乎return, 和do- 特定构造(一些含糖绑定(>>=))都来自Monad IO. 假设这是正确的,我的实际问题是:

为什么Applicative IO实现依赖于Monad IO函数/组合器?

不是比Applicative 强大的概念Monad吗?


编辑(一些澄清):

这种实现违背了我的直觉,因为根据 Typeclassopedia 文章,在可以制作给定类型Applicative 之前Monad需要它(或者理论上应该)。

4

5 回答 5

12

(...)根据 Typeclassopedia 文章,给定类型必须是 Applicative 才能成为 Monad (或者理论上应该如此)。

是的,您的括号旁正是这里的问题。理论上, any也Monad 应该Applicative,但这实际上不是必需的,因为历史原因(即,因为Monad已经存在了更长的时间)。这也不是 的唯一特点Monad

考虑相关类型类的实际定义,取自baseHackage 上的包源。

这是Applicative

class Functor f => Applicative f where
    pure  :: a -> f a
    (<*>) :: f (a -> b) -> f a -> f b
    (*>)  :: f a -> f b -> f b
    (<*)  :: f a -> f b -> f a

...我们可以观察到以下几点:

  • 给定当前存在的类型类,上下文是正确的,即它需要Functor.
  • 它是根据函数应用定义的,而不是(从数学角度可能更自然)提升元组的术语。
  • 它包括技术上多余的运算符,相当于提升常量函数。

同时,这里是Monad

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

...我们可以观察到以下几点:

  • 上下文不仅忽略Applicative,而且忽略 ,Functor这两者在逻辑上都被隐含Monad但未明确要求。
  • 它也是根据函数应用定义的,而不是使用returnand的更自然的数学定义join
  • 它包括一个技术上多余的运算符,相当于提升一个常量函数。
  • 它还包括fail根本不适合的内容。

一般来说,Monad类型类与其所基于的数学概念的不同之处可以追溯到其作为编程抽象的历史。有些,比如它所共有的函数应用偏见Applicative,是函数式语言中存在的反映;其他人,喜欢fail或​​缺乏适当的阶级背景,是历史上的偶然事件。

这一切都归结为拥有一个 的实例Monad意味着一个 的实例Applicative,这反过来又意味着一个 的实例Functor。类上下文只是明确地形式化了这一点;无论如何,它仍然是正确的。就目前而言,给定一个Monad实例,两者FunctorApplicative都可以以完全通用的方式定义。与更通用的完全相同的意义上Applicative“没有那么强大” :如果您复制+粘贴通用实例,则 Any是自动的,但是存在不能定义为的实例。MonadMonadApplicativeApplicativeMonad

一个类上下文,就像Functor f => Applicative f说两件事:后者暗示前者,并且必须存在一个定义来实现该暗示。在许多情况下,无论如何定义后者都会隐式定义前者,但编译器通常无法推断出这一点,因此需要显式写出这两个实例。可以用Eqand观察到同样的事情Ord——后者显然暗示了前者,但您仍然需要定义一个Eq实例才能为Ord.

于 2011-07-03T23:15:29.287 回答
9

Haskell 中的IO类型是抽象的,所以如果你想实现一个通用Applicative的,IO你必须使用 IO 支持的操作来完成。由于您可以Applicative根据Monad操作来实现这似乎是一个不错的选择。你能想出另一种方法来实现它吗?

是的,Applicative在某种意义上不如Monad.

于 2011-07-03T18:47:20.297 回答
7

Applicative不是比 Monad更强大的概念吗?

是的,因此只要你有一个Monad,你总是可以把它变成一个Applicative。您可以在示例中替换IO为任何其他 monad,这将是一个有效的Applicative实例。

打个比方,虽然彩色打印机可能被认为比灰度打印机更强大,但您仍然可以使用它来打印灰度图像。

当然,也可以基于 an和 set来创建Monad实例,但您无法进行一般定义。这就是更强大的意思。Applicativereturn = pure>>=Monad

于 2011-07-03T19:36:09.407 回答
3

在一个完美的世界中,每个Monad都是一个Applicative(所以我们有class Applicative a => Monad a where ...),但由于历史原因,两个类型类都是独立的。所以你观察到这个定义有点“倒退”(使用更强大的抽象来实现不太强大的抽象)是正确的。

于 2011-07-03T21:01:41.247 回答
1

对于旧版本的 GHC,您已经有了非常好的答案,但在最新版本中,您确实有class Applicative m => Monad m,所以您的问题需要另一个答案。

就 GHC 实现而言:GHC 只是在尝试编译任何给定类型之前检查为给定类型定义了哪些实例。

就代码语义而言:class Applicative m => Monad m并不意味着Applicative必须“首先”定义实例,只是如果在程序结束时尚未定义实例,则编译器将中止。

于 2015-10-28T05:02:10.653 回答