我有一个名为TaskMonad
的Monad,定义如下:
data TaskMonad a = TaskMonad (Environment -> (TaskResult a, Environment))
其中Environment
是记录类型,TaskResult
是ADT;但它们对这个问题并不重要。
我已为Functor
定义了Applicative
,Monad
和TaskMonad
个实例,我现在希望能够将此monad与其他Monad结合使用(例如{{1}所以我定义了一个新类型如下:
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
块tma :: TaskMonad a
内有do
monad。现在我想做的是,以某种方式调用m
的{{1}}实例,这样我就可以获得>>=
的结果,类型TaskMonad
的值,这样我就可以参数化了tma
用它来获取值a
。但我在tb
monad的背景下,我遇到了各种各样的问题。
如何获取Task b
的结果以将其提供给m
?
答案 0 :(得分:2)
好吧,我不知道这有多大帮助,但是如果你真的从第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'')
可能还有一种方法可以按照您最初的预期方式执行此操作,但我非常确定原始Task
也需要更改为初始Environment
。
我认为你在monad中实际上做的不仅仅是State
,所以需要将它放在各自的实例中,但我认为这个框架应该会有所帮助。
当然,您是否需要使用非变压器版本,只需将Identity
传递给m
。
我知道Applicative
实例的实现没有意义,但是我在没有ApplicativeDo
的旧GHC上构建它,实际上最简单的做法就是设置傻约束那里。
答案 1 :(得分:2)
如@ BartekBanachewicz的回答所述,将monad m
置于->
内是可行的方法。
我认为不可能通过m (TaskMonad a)
以你想要的方式去做,至少不是一般的。一般来说monads aren't closed under composition就是这种情况的一个例子。
让我举一个简化的例子(它需要一些理论):让我们使用读者monad而不是状态monad,让我们放弃TaskResult
并让环境作为类型参数。因此TaskMonad
只是m (r -> a)
。现在让我们假设它是一个单子,然后有
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
或>>=
以某种方式特定于{{1},则无效}。所以你可以在这种情况下做到这一点,但我猜你会遇到其他问题阻止你得到一个正确的非平凡的Environment
实例。