我有一个名为TaskMonad的Monad,定义如下:
data TaskMonad a = TaskMonad (Environment -> (TaskResult a, Environment))其中Environment是一种记录类型,TaskResult是一种ADT;但它们对问题并不重要。
我已经为TaskMonad定义了Functor、Applicative和Monad实例,现在我希望能够将这个monad与其他monad(例如IO)组合在一起,所以我定义了一个新类型,如下所示:
newtype Task m a = Task { runTask :: m (TaskMonad a) }我对Functor和Applicative的定义如下:
instance Monad m => Functor (Task m) where
fmap f ta = Task $ do tma <- runTask ta
return (fmap f tma)
instance Monad m => Applicative (Task m) where
pure = Task . return . return
(<*>) prod tx = Task $ do tmprod <- runTask prod
tmtx <- runTask tx
return (tmprod <*> tmtx)我还让Task成为MonadTrans类的成员:
instance MonadTrans Task where
lift = Task . (liftM return)到目前为止还不错(或者至少它编译了..),但是现在我想定义Monad的实例,但是我在这里遇到了问题:
instance Monad m => Monad (Task m) where
return = pure
(>>=) ta tb = ...我尝试了多种方法,大多数尝试都是这样开始的:
(>>=) ta tb = Task $ do tma <- runTask ta 现在,我们在m monad的do块中有了tma :: TaskMonad a。现在我想做的是,以某种方式调用TaskMonad的>>=实例,这样我就可以获得tma的结果,一个a类型的值,这样我就可以用它来参数化tb,以获得Task b的值。但是我处在m monad的上下文中,我遇到了各种各样的问题。
如何获取tma的结果并将其提供给tb?
发布于 2016-04-25 21:33:26
好吧,我不知道这有多大帮助,但是如果你实际上从第0天(在TaskMonad中)开始使用一个转换器,你可以这样做:
data TaskMonad m a = TaskMonad (Environment -> m (TaskResult a, Environment)) deriving Functor
instance Monad m => Monad (TaskMonad m) where
return = pure
(TaskMonad f) >>= b = TaskMonad $ \e -> do
(TaskResult r, e') <- f e
let (TaskMonad g) = b r
g e'
instance (Monad m, Functor m) => Applicative (TaskMonad m) where
pure a = TaskMonad $ \e -> return (TaskResult a, e)
(TaskMonad f) <*> (TaskMonad g) = TaskMonad $ \e -> do
(TaskResult f', e') <- f e
(TaskResult a, e'') <- g e'
return (TaskResult (f' a), e'')也许还有一种方法可以达到你最初想要的效果,但我敢肯定,为了获得初始的Environment,也需要修改原始的Task。
我假设您实际上在monad中比State做了更多的事情,因此需要将其放在各自的实例中,但我认为框架应该会有所帮助。
当然,如果您需要使用非转换器版本,只需传入m的Identity即可。
免责声明:
我知道这个Applicative实例的实现没有意义,但我是在没有ApplicativeDo的旧GHC上构建的,这实际上是将愚蠢的约束放在那里的最简单的事情。
发布于 2016-04-26 04:24:07
正如@BartekBanachewicz的答案中所描述的,将monad m放入->中是可行的。
我相信使用m (TaskMonad a)是不可能做到你想要的方式的,至少不是泛泛的。一般来说,monads aren't closed under composition就是这种情况的一个例子。
让我给出一个简单的例子(需要一些理论):让我们使用阅读器monad而不是状态monad,让我们丢弃TaskResult,让我们将环境作为类型参数。因此,TaskMonad将只是m (r -> a)。现在让我们假设它是一个monad,然后有
join :: m (r -> (m (r -> a))) -> m (r -> a)将a专门化到Void (另请参阅Bottom type)和m专门化到Either r
join :: Either r (r -> (Either r (r -> Void))) -> Either r (r -> Void)但之后我们就可以构建
doubleNegationElimination :: Either r (r -> Void)
doubleNegationElimination = join (Right Left)作为Right Left :: Either r (r -> Either r (r -> Void))。通过Curry-Howard isomorphism,这意味着我们将能够在直觉逻辑中证明Double negation elimination,这是一个矛盾。
你的情况有点复杂,但也可能有类似的论点。唯一的缺陷是,我们假设“环境”部分r是通用的,所以如果您的join或>>=是特定于Environment的,那么它将无法工作。因此,在这种情况下,您也许能够做到这一点,但我猜您会遇到其他问题,阻碍您获得适当的非平凡的Monad实例。
https://stackoverflow.com/questions/36840728
复制相似问题