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

gor*_*sky 10 monads haskell applicative

在"为了好大学而学习你的哈斯克尔!" 作者声称Applicative IO实例是这样实现的:

instance Applicative IO where
    pure = return
    a <*> b = do
        f <- a
        x <- b
        return (f x)
Run Code Online (Sandbox Code Playgroud)

我可能是错的,但似乎都returndo特异性结构(一些加糖的结合(>>=))来自Monad IO.假设这是正确的,我的实际问题是:

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

是不是没有Applicative 那么强大的概念Monad


编辑(一些澄清):

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

C. *_*ann 12

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

是的,你的括号旁边就是这里的问题.理论上,任何一个也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
Run Code Online (Sandbox Code Playgroud)

......我们可以观察以下内容:

  • 给定当前存在的类型类,即上下文是正确的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
Run Code Online (Sandbox Code Playgroud)

......我们可以观察以下内容:

  • 上下文不仅忽略了Applicative,而且Functor两者都在逻辑上暗示,Monad但没有明确要求.
  • 它也是根据函数应用来定义的,而不是使用return和更加数学上自然的定义join.
  • 它包括一个技术上多余的操作员,相当于提升一个恒定的功能.
  • 它还包括fail根本不适合的.

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

这一切归结为具有一个Monad暗示实例的实例Applicative,而实例又暗示了一个实例Functor.类上下文只是明确地将其形式化; 无论如何都是如此.因为它的立场,给定一个Monad实例,都FunctorApplicative在一个完全通用的方式来定义.Applicative它是"不那么强大",而不是Monad完全相同的意义,它更通用:如果你复制+粘贴通用实例,Monad则会自动执行Applicative,但是存在Applicative无法定义为的实例Monad.

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

  • `Monad`现在是`Applicative`的子类. (3认同)
  • @gorsky:向后兼容性是最大的障碍,与类型类如何交互的一般不灵活性密切相关.请参阅[这个问题](http://stackoverflow.com/questions/5730270),讨论为什么它很尴尬,包括我自己对此事的看法. (2认同)

aug*_*tss 9

IOHaskell中的类型是抽象的,因此如果要实现通用Applicative,IO则必须使用IO支持的操作.既然你可以ApplicativeMonad操作方面实现,这似乎是一个不错的选择.你能想到另一种实现它的方法吗?

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

  • 这是一个选择问题,但我认为这是一个不错的选择.您可以为`IO`提供`Functor`,`Applicative`和`Monad`的操作(因为IO是抽象的).或者你可以提供`Monad`操作并用'Monad`来实现其他操作.后者对我更有意义,因为你需要提供更少的基本操作.提供尽可能强大的原语以减少数量是有意义的. (6认同)
  • @augustss:虽然,有些情况下`(<*>)`可以比使用`(>> =)`的泛型实现更有效地实现.无限流是一个明显的例子,通用的'ap`必须构造笛卡尔积,然后取对角线,而`(<*>)`可以简单地表现得像'zip`. (4认同)

ham*_*mar 7

不是应用比Monad 强大的概念吗?

是的,因此无论什么时候有Monad你,你都可以随时做到Applicative.您可以替换IO示例中的任何其他monad,它将是一个有效的Applicative实例.

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

当然,也可以将一个Monad实例基于Applicative和设置return = pure,但是你无法>>=一般地定义.这是Monad更有力的手段.