Haskell 应用IO是否基于Monad IO中的函数实现?

Haskell 应用IO是否基于Monad IO中的函数实现?,haskell,monads,applicative,Haskell,Monads,Applicative,在“向您学习Haskell的好处”一文中,作者声称应用IO实例的实现如下: instance Applicative IO where pure = return a <*> b = do f <- a x <- b return (f x) 实例应用程序IO,其中 纯=返回 a=b=do f=))来自Monad IO。假设这是正确的,我的实际问题是: 为什么应用IO的实现取决于Monad IO函数/组合器

在“向您学习Haskell的好处”一文中,作者声称
应用IO
实例的实现如下:

instance Applicative IO where
    pure = return
    a <*> b = do
        f <- a
        x <- b
        return (f x)
实例应用程序IO,其中
纯=返回
a=b=do
f=)
)来自
Monad IO
。假设这是正确的,我的实际问题是:

为什么
应用IO
的实现取决于
Monad IO
函数/组合器?

是不是
的应用性
不如
Monad
强大


编辑(一些澄清):


这个实现违背了我的直觉,因为根据Typeclassopedia文章的说法,在生成
Monad
(或者理论上应该是这样)之前,给定的类型必须是
Applicative

IO
类型在Haskell中是抽象的,因此,如果您想要为
IO
实现一个通用的
Applicative
,您必须使用IO支持的操作来实现它。因为您可以在
Monad
操作方面实现
Applicative
,这似乎是一个不错的选择。你能想出另一种方法来实现它吗

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

应用程序不是一个不如Monad强大的概念吗

是的,因此,只要您有一个
Monad
,您就可以使它成为一个
应用程序。您可以用示例中的任何其他monad替换
IO
,它将是一个有效的
Applicative
实例

作为类比,虽然彩色打印机可能被认为比灰度打印机功能更强大,但您仍然可以使用彩色打印机打印灰度图像

当然,也可以将
Monad
实例建立在
应用程序的基础上,并设置
return=pure
,但通常无法定义
>=
。这就是
Monad
更强大的含义。

在每个
Monad
都是
应用的
(因此我们有
类Applicative a=>Monad a where…
),但由于历史原因,两个类型类都是独立的。因此,您认为这个定义有点“倒退”(使用更强大的操作来实现较弱的操作)是正确的

(…)根据Typeclassopedia文章,在将给定类型设置为Monad(或者在理论上应该是Monad)之前,它必须是可应用的

是的,你的旁白正是这里的问题所在。理论上,任何
Monad
都应该是一个
应用程序
,但由于历史原因(即,因为
Monad
已经存在了更长的时间),这实际上不是必需的。这也不是Monad的唯一特性

考虑相关类型类的实际定义,这些定义取自Hackage上
base
包的源代码

下面是
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
…关于这一点,我们可以观察到以下几点:

  • 上下文不仅忽略了
    Applicative
    ,还忽略了
    Functor
    ,这两个函数在逻辑上都是由
    Monad
    隐含的,但不是明确需要的
  • 它还根据函数应用进行定义,而不是使用
    return
    join
    进行更自然的数学定义
  • 它包括一个技术上多余的操作符,相当于提升一个常量函数
  • 它还包括根本不适合的
    fail
一般来说,
Monad
类型类不同于它所基于的数学概念的方式可以追溯到它作为编程抽象的历史。一些,如它与
Applicative
共享的函数应用程序偏见,是函数语言中存在的一种反映;其他原因,如
失败
或缺乏适当的类上下文,则是历史事故

归根结底,拥有一个
Monad
的实例意味着一个
Applicative
的实例,这反过来又意味着一个
Functor
的实例。类上下文只是显式地将其形式化;不管怎样,这都是事实。目前,给定一个
Monad
实例,
Functor
Applicative
都可以用一种完全通用的方式定义
Applicative
Monad
功能“弱”,与它更通用的意义完全相同:如果复制并粘贴通用实例,任何
Monad
都会自动
Applicative
,但存在不能定义为
Monad
Applicative
实例


类上下文,如
函子f=>Applicative f
说明了两件事:后者意味着前者,并且必须存在一个定义来实现这一含义。在许多情况下,定义后者会隐式地定义前者,但编译器通常无法推断出这一点,因此需要显式地写出两个实例。同样的事情可以在
Eq
Ord
中观察到——后者显然意味着前者,但您仍然需要定义
Eq
实例,以便为
Ord

定义一个实例,您已经对较旧版本的GHC有了非常好的答案,但在最新版本中,实际上您有
类Applicative m=>Monad m
,因此您的问题需要另一个答案

就GHC实现而言:GHC在尝试编译任何实例之前,只检查为给定类型定义的实例

就代码语义而言:
class Applicative m=>Monad m
并不意味着必须“首先”定义
Applicative
实例,只是如果在程序结束时没有定义它,那么
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