我正在阅读有关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。该变压器可以使用吗?
答案 0 :(得分:6)
Applicative
实例是合法的。实际上,它是与Compose
IO (Either e)
相同的实例。可应用函子的组成是可应用的(这是应用程序具有单子所没有的很好的东西之一)。
但是,the documentation的“法律”部分也列出了以下内容:
如果
f
也是Monad
,它应该满足pure = return (<*>) = ap (*>) = (>>)
这就是问题所在,因为不可能有与给定应用程序相对应的monad(这是有关RHS有条件执行的注释起作用的地方)。因此,Applicative
和Monad
实例虽然都是孤立的,但它们是合法的,但他们不同意,因此应处以死刑。