首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Monad Transformer:由于不同的Monad定义绑定的麻烦

Monad Transformer:由于不同的Monad定义绑定的麻烦
EN

Stack Overflow用户
提问于 2016-04-25 20:34:13
回答 2查看 74关注 0票数 2

我有一个名为TaskMonad的Monad,定义如下:

代码语言:javascript
复制
data TaskMonad a = TaskMonad (Environment -> (TaskResult a, Environment))

其中Environment是一种记录类型,TaskResult是一种ADT;但它们对问题并不重要。

我已经为TaskMonad定义了FunctorApplicativeMonad实例,现在我希望能够将这个monad与其他monad(例如IO)组合在一起,所以我定义了一个新类型,如下所示:

代码语言:javascript
复制
newtype Task m a = Task { runTask :: m (TaskMonad a) }

我对FunctorApplicative的定义如下:

代码语言:javascript
复制
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类的成员:

代码语言:javascript
复制
instance MonadTrans Task where
    lift = Task . (liftM return)

到目前为止还不错(或者至少它编译了..),但是现在我想定义Monad的实例,但是我在这里遇到了问题:

代码语言:javascript
复制
instance Monad m => Monad (Task m) where
    return      = pure
    (>>=) ta tb = ...

我尝试了多种方法,大多数尝试都是这样开始的:

代码语言:javascript
复制
(>>=) ta tb = Task $ do tma <- runTask ta 

现在,我们在m monad的do块中有了tma :: TaskMonad a。现在我想做的是,以某种方式调用TaskMonad>>=实例,这样我就可以获得tma的结果,一个a类型的值,这样我就可以用它来参数化tb,以获得Task b的值。但是我处在m monad的上下文中,我遇到了各种各样的问题。

如何获取tma的结果并将其提供给tb

EN

回答 2

Stack Overflow用户

发布于 2016-04-25 21:33:26

好吧,我不知道这有多大帮助,但是如果你实际上从第0天(在TaskMonad中)开始使用一个转换器,你可以这样做:

代码语言:javascript
复制
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做了更多的事情,因此需要将其放在各自的实例中,但我认为框架应该会有所帮助。

当然,如果您需要使用非转换器版本,只需传入mIdentity即可。

免责声明:

我知道这个Applicative实例的实现没有意义,但我是在没有ApplicativeDo的旧GHC上构建的,这实际上是将愚蠢的约束放在那里的最简单的事情。

票数 2
EN

Stack Overflow用户

发布于 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,然后有

代码语言:javascript
复制
join :: m (r -> (m (r -> a))) -> m (r -> a)

a专门化到Void (另请参阅Bottom type)和m专门化到Either r

代码语言:javascript
复制
join :: Either r (r -> (Either r (r -> Void))) -> Either r (r -> Void)

但之后我们就可以构建

代码语言:javascript
复制
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实例。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/36840728

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档