为什么此适用实例不合法?

时间:2019-01-06 03:36:43

标签: haskell applicative

我正在阅读有关monad变压器的信息,并且发现了这篇显然知名的文章-A Gentle Introduction to Monad Transformers。引起我注意的是作者在其中描述了一个临时ExceptT变压器的适用实例,但警告该实例是非法的。

代码如下:

data EitherIO e a = EitherIO {
    runEitherIO :: IO (Either e a)
}

instance Functor (EitherIO e) where
    fmap f = EitherIO . fmap (fmap f) . runEitherIO

instance Applicative (EitherIO e) where
    pure    = EitherIO . return . Right
    f <*> x = EitherIO $
        liftA2 (<*>)
            (runEitherIO f)
            (runEitherIO x)

警告:

  

警告:非常有远见的读者向我指出,这种适用性实例是非法的。具体而言,它无条件地执行右侧的副作用。对合法实例的期望是,只有在左侧操作成功的情况下,才应执行右侧的副作用。

我假设特别是<*>的实现是问题所在。

所以我的主要问题是:该实例不完全符合哪些法律?

据我所知,这四个适用法律都得到了满足(我当然可能错了)。作者说,问题在于即使左侧操作不是成功的操作,我也会执行右侧的副作用(我假设是<*>的右侧)。 ”表示IO操作在执行时将产生一个Right值。

虽然从使用角度来看,这是有道理的,但仍然有启发性的是,这里确切不满足哪些法律以及为什么不满足。

此外,关于实例为何非法的解释提到了副作用,这使得推理类型仅适用于IO monad?但是,在本文的最后,作为最后的一步,我们将IO monad更改为一般monad,并使其成为所描述数据类型的参数。这就引出了另一个问题:如果我们想像自己是在写这个Monad转换器,我需要运用哪种推理来发现所描述的应用实例确实是非法的,而无需诉诸于特定的Monad。该变压器可以使用吗?

1 个答案:

答案 0 :(得分:6)

Applicative实例是合法的。实际上,它是与Compose IO (Either e)相同的实例。可应用函子的组成是可应用的(这是应用程序具有单子所没有的很好的东西之一)。

但是,the documentation的“法律”部分也列出了以下内容:

  

如果f也是Monad,它应该满足

pure = return
(<*>) = ap
(*>) = (>>)

这就是问题所在,因为不可能有与给定应用程序相对应的monad(这是有关RHS有条件执行的注释起作用的地方)。因此,ApplicativeMonad实例虽然都是孤立的,但它们是合法的,但他们不同意,因此应处以死刑。