应用IO是否基于Monad IO的功能实现?

时间:2011-07-03 18:31:02

标签: haskell monads applicative

在“向大家学习哈斯克尔!”作者声称Applicative IO实例是这样实现的:

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

我可能错了,但似乎returndo特定结构(某些含糖的(>>=)绑定)来自Monad IO。假设这是正确的,我的实际问题是:

为什么Applicative IO实施取决于Monad IO函数/组合器?

Applicative相比,Monad 是否更强大概念?


修改(一些澄清):

这种实现违背了我的直觉,因为根据Typeclassopedia的文章,在之前,某个类型需要Applicative 才能生成Monad(或者它 理论上)。

5 个答案:

答案 0 :(得分:12)

  

(...)根据Typeclassopedia文章,在给定类型为Monad之前,它是必需的(或者它应该是理论上的。)

是的,你的括号旁边就是这里的问题。理论上,任何Monad 也应该是Applicative,但由于历史原因(因为Monad已经存在更长时间),实际上并不需要这样做。这也不是Monad的唯一特性。

考虑相关类型类的实际定义,取自base软件包在Hackage上的来源。

这是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,还忽略了FunctorMonad逻辑上暗示了这两者,但没有明确要求。
  • 它也是根据功能应用程序定义的,而不是使用returnjoin在数学上更自然的定义。
  • 它包括一个技术上多余的操作符,相当于提升常量函数。
  • 它还包括fail,它根本不适合。

一般来说,Monad类型类与它所基于的数学概念不同的方式可以追溯到它作为编程抽象的历史。有些像它与Applicative共享的函数应用程序偏差一样,是功能语言中存在的反映;其他人,如fail或缺乏适当的阶级背景,最重要的是历史事故。

归结为具有Monad实例的意味着Applicative的实例,而后者又暗示了Functor 的实例。类上下文只是明确地将其形式化;无论如何都是如此。就目前而言,给定Monad实例,FunctorApplicative都可以以完全通用的方式定义。 Applicative的“功能不如Monad完全相同,而且一般:如果您复制,Monad会自动Applicative +粘贴通用实例,但存在Applicative个实例,无法将其定义为Monad

Functor f => Applicative f这样的类上下文说了两件事:后者暗示了前者,并且必须存在一个定义来实现这个含义。在许多情况下,定义后者无论如何都隐含地定义了前者,但编译器通常不能推断出这种情况,因此需要明确地写出两个实例。使用EqOrd可以观察到同样的事情 - 后者显然暗示前者,但您仍然需要定义Eq实例以便为{{1}定义一个实例}。

答案 1 :(得分:9)

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

是的,Applicative在某种程度上不如Monad强大。

答案 2 :(得分:7)

  

是不是应用较少强大的概念而不是Monad?

是的,因此只要您有Monad,就可以随时将其设为Applicative。您可以将IO替换为示例中的任何其他monad,它将是一个有效的Applicative实例。

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

当然,也可以将Monad实例基于Applicative并设置return = pure,但您通常无法定义>>=。这就是Monad更强大的意思。

答案 3 :(得分:3)

perfect world中,Monad每个Applicative都是class Applicative a => Monad a where ...(因此我们有{{1}}),但由于历史原因,这两个类型都是独立的。所以你观察到这个定义有点“向后”(使用更强大的抽象来实现功能较弱的定义)是正确的。

答案 4 :(得分:1)

对于旧版本的GHC,您已经拥有完美的答案,但在最新版本中,您确实拥有class Applicative m => Monad m,因此您的问题需要另一个答案。

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

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