Haskell 应用IO是否基于Monad IO中的函数实现?
在“向您学习Haskell的好处”一文中,作者声称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函数/组合器
应用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