为什么MonadIO特定于IO,而不是更通用的MonadTrans?

CR *_*ost 8 io haskell types monad-transformers

所以在transformers我看来,

class (Monad m) => MonadIO m where
    -- | Lift a computation from the 'IO' monad.
    liftIO :: IO a -> m a

instance MonadIO IO where
    liftIO = id
Run Code Online (Sandbox Code Playgroud)

我明白,这与MonadTrans不同的原因是,如果你有一些M1T (M2T (M3T (M4T IO))) x由4个组成的monad变换器组成,那么你不想,lift . lift . lift . lift $ putStrLn "abc"但你宁愿只是liftIO $ putStrLn "abc".

但是,IO当上面的基本定义似乎是这种奇怪的递归时,这种特殊性似乎非常奇怪liftIO.似乎应该为某些组合器设置一个newtype声明,(ExceptT :~: MaybeT) IO x这样lift你就可以得到一个单独的(我想这是一个monad变换器变换器吗?),或者是一些多参数类型,

class (Monad m) => MonadEmbed e m
    -- | Lift a computation from the `e` monad.
    embed :: e a -> m a

instance (Monad m) => MonadEmbed m m where
     embed = id
Run Code Online (Sandbox Code Playgroud)

为什么不transformers使用这些方法之一,以便MonadTrans序列不必根植IO?只是事实上变形金刚处理所有"其他"效果,以便最底层的唯一东西要么Identity(已经处理过return :: a -> m a)还是IO?或者上面是否需要像transformers图书馆不愿意包含的UndecidableInstances ?或者是什么?

Dan*_*ner 7

但是,这种特殊性IO似乎非常奇怪

我质疑这是特定的假设IO.我还看到很多其他课程mtl.例如:

class Monad m => MonadError e m | m -> e where
    throwError :: e -> m a
    catchError :: m a -> (e -> m a) -> m a

class Monad m => MonadState s m | m -> s where
    get :: m s
    put :: s -> m ()
    state :: (s -> (a, s)) -> m a
Run Code Online (Sandbox Code Playgroud)

......还有很多其他人.通常,mtl构建monadic动作的" 方法"是使用这些类型类多态操作,这样就不需要lift- 而是只需将操作单态化为适当的提升类型.例如,MonadError完全取代假设liftMaybe :: MonadMaybe m => Maybe a -> m a:而不是提升Maybe a值,人们将拥有Maybe a值调用的生成者throwErrorreturn不是NothingJust.

对于某些组合器来说似乎应该有一个newtype声明,这样你(ExceptT :~: MaybeT) IO x就可以得到一个单一的升力

有了这个提议,你需要(至少)两种不同类型的升降机:一部电梯从去m atrans m a,和一部电梯从去trans m a(trans' :~: trans) m a.具有处理两种提升的单个操作更均匀.

好像应该有一些多参数类型,

class Monad m => MonadEmbed e m
    -- | Lift a computation from the `e` monad.
    embed :: e a -> m a

instance Monad m => MonadEmbed m m where
    embed = id
Run Code Online (Sandbox Code Playgroud)

这种方法起初看起来很不错.但是,如果您尝试编写并使用此类,您将很快发现为什么所有mtl类都包含函数依赖项:实例MonadEmbed m m难以选择!即便是一个非常简单的例子

embed (Right ()) :: Either String ()
Run Code Online (Sandbox Code Playgroud)

是一个含糊不清的错误.(毕竟,我们只知道Right 3 :: Either a ()对于某些人a- 我们还不知道,a ~ String所以我们不能选择MonadEmbed m m实例!)我怀疑你的大多数其他实例都会遇到类似的问题.如果你添加明显的函数依赖关系,你的类型推理问题就会消失,但是fundep检查会对你产生很大的限制:你可能只会从基础monad中提升,而不是像你希望的那样从任意中间monad中解除.这在实践中是一个非常痛苦的问题(并且" mtl方式"疼痛是如此之小),以至于没有完成mtl.

也就是说,您可能喜欢使用该transformers-base包装.

只是事实上变形金刚处理所有"其他"效果,以便最底层的唯一东西要么Identity(已经处理过return :: a -> m a)还是IO

正如你所说,最常见的基础是IO(我们已经拥有MonadIO)或Identity(通常只使用return一个纯粹的计算而不是一个提升的monadic计算).有时ST是一个方便的基础monad,但使用变换器ST比使用它们更为罕见IO.


lef*_*out 5

这些Monad...类似乎主要是为了直接推广 monad的特征操作,如果monad被埋在一堆变换器下也能工作.这些操作通常是没有这么大集,例如State只需要get,put,state而且 modify如果你想要的,但仅此而已.

不是这样IO; 制作类中过多的基本IO操作方法是相当不切实际的MonadIO.你当然可以获得所有这些,如果你只是liftIO作为一个"转换函数"介绍,但我打赌这总是被认为是一个黑客.

此外:IO是最重要的非平凡基础单子; 仅仅因为这个原因,我不认为给它一个专用的升力功能是不合适的.

关于你的"单一升力是你所需要的"想法:问题:~:在于变换器现在形成二叉树而不是具有清晰层次结构的简单堆栈.这使得mtl类的整个想法更加成问题.