为什么MFunctor的“提升”没有“Monad n”约束?

时间:2014-01-27 21:12:04

标签: haskell

我有一个协程转换器

data Step y m a = Done a | Yield y (CoT y m a)

data CoT y m a = CoT (m (Step y m a))

使用Monad实例

unCoT :: CoT y m a -> m (Step y m a)
unCoT (CoT m) = m

instance Monad m => Monad (CoT y m) where
    return  = CoT . return . Done
    CoT x >>= f = CoT $ do
      x' <- x
      case x' of
        Done a -> unCoT (f a)
        Yield y x' -> return (Yield y (x' >>= f))

如果我使用MFunctor Monad m约束定义Monad n课程,我可以定义hoist

class MFunctor t where
  hoist :: (Monad n, Monad m) => (forall a. m a -> n a) -> t m b -> t n b

instance MFunctor (CoT y) where
  hoist f (CoT m) = CoT $ do
    step <- f m
    return (case step of Done x     -> Done x
                         Yield y m' -> Yield y (hoist f m'))

mmorph的{​​{1}}只有hoist约束。我可以在没有它的情况下定义Monad m,还是缺少hoist的一般性?

编辑:我知道 可能!但我的问题仍然存在:我们确定这里不乏普遍性吗?

MFunctor

2 个答案:

答案 0 :(得分:8)

mmorph是在pipes-3.*系列(曾经是an internal pipes module)的背景下开发的,它具有以下功能:

raise
    :: (Monad m, MFunctor t1, MonadTrans t2)
    => t1 m r -> t1 (t2 m) r
raise = hoist lift

如果您将Monad n约束添加到hoist,则必须向Monad (t2 m)添加raise约束。我通常会尽量减少库中的约束,但找不到需要MFunctor约束的Monad n个实例,所以我将其删除了。

附注:CoT y m a与来自Producer y m a的{​​{1}}相同,后者已有pipes个实例。

答案 1 :(得分:1)

您可以使用任何类型t,您可以将hoist' :: (Monad m, Monad n) => (forall t. m t -> n t) -> t m a -> t n a定义为MFunctor。但是,如果t n a上有Monad个实例,您将只能使用生成的n。我们通过推迟自然变换的应用来做到这一点。或者用一种奇特的方式说这就是应用coyoneda引理。

{-# LANGUAGE RankNTypes, GADTs #-}

import Control.Monad.Morph

-- Slightly weaker than MFunctor due to the monad constraint on n.
class MFunctor' t where
  hoist' :: (Monad m, Monad n) => (forall b. m b -> n b) -> t m a -> t n a

data MCoyoneda t n a where
  MCoyoneda :: Monad m => (forall b. m b -> n b) -> t m a -> MCoyoneda t n a

liftMCoyoneda :: Monad m => t m a -> MCoyoneda t m a
liftMCoyoneda = MCoyoneda id

lowerMCoyoneda' :: (MFunctor' t, Monad n) => MCoyoneda t n a -> t n a
lowerMCoyoneda' (MCoyoneda f tma) = hoist' f tma

-- The result is actually slightly stronger than 'MFunctor', as we do not need
-- a monad for 'm' either.
hoistMCoyoneda :: (forall b. m b -> n b) -> MCoyoneda t m a -> MCoyoneda t n a
hoistMCoyoneda f (MCoyoneda trans tma) = MCoyoneda (f . trans) tma

instance MFunctor (MCoyoneda t) where
  hoist = hoistMCoyoneda

因此,我认为我们没有普遍性,因为如果您无法为MFunctor实施实例,那么Monad上的lowerMCoyoneda'约束就不会丢失任何内容。

我发现这会遇到similar problem with RVarT