【发布时间】:2019-12-19 10:25:17
【问题描述】:
我最近决定开始使用 monad 转换而不是堆叠我的 monad,因为这似乎是正确的做法。无论如何,我之前并没有真正堆叠很多单子。我得到(我认为)它背后的想法和 lift 函数,据我所知,它充当了转换的一种 return (将一些来自底层 monad 的东西放入转换后的 monad)。
到目前为止一切都很好,但我没有看到任何类似于 fmap 用于单子转换的函数。让我给你举个例子。假设我有一个自定义 monad,m,并且我在其上使用了 StateT 转换,因此使用类型 StateT s m a 而不是 m (State s a)。
现在,碰巧在我的 monad m 中,我有一个函数可以转换 monadic 元素(实际上它是 monad 的构造函数之一,如果您需要我可以提供的详细信息),同时保留一些感知潜在价值:myFunc :: m a -> m a。
所以我正在构建一个递归函数recFunc :: State s a -> [t] -> m (State s a),看起来类似于这样:
recFunc :: State s a -> [t] -> m (State s a)
recFunc x [] = return x
recFunc x (t:ts) = myFunc (recFunc x ts)
但是如果我尝试使用 monad 转换来复制它,我会遇到问题,因为我找不到将 myFunc 插入到混合中的方法。将输入写为State s a 还是StateT s Identity a 都没有关系(代数上哪个更精确?)
recFuncT :: StateT s Identity a -> [t] -> StateT s m a
recFuncT x [] = ???
recFuncT x (t:ts) = ????? where rec = recFuncT x ts
所以我正在寻找类似(发明,如果可能的话我不知道如何实现)以下功能:
transmap :: (MonadTrans t, Monad m) => (forall b. m b -> m b) -> t m a -> t m a
transmap = ???
transreturn :: (MonadTrans t, Monad m) => m (t Identity a) -> t m a
transreturn = ???
我觉得我应该能够使用 lift 定义这些,但老实说,我不知道如何。
如果我有它们,那么我可以这样做:
recFuncT :: StateT s Identity a -> [t] -> StateT s m a
recFuncT x [] = transreturn (return x)
recFuncT x (t:ts) = transmap myFunc (recFuncT x ts)
也许我真正想要的是更基本的东西。我希望t m a 和m (t Identity a) 之间假定的同构是明确的,所以我正在寻找函数:
fromTrans :: t m a -> m (t Identity a)
toTrans :: m (t Identity a) -> t m a
据我了解 monad 转换器,这些函数应该始终存在并且相当简单,对吧?
有了这些,我显然可以实现transmap 和transreturn:
transmap :: (MonadTrans t, Monad m) => (forall b. m b -> m b) -> t m a -> t m a
transmap f x = toTrans (f (fromTrans x))
transreturn :: (MonadTrans t, Monad m) => m (t Identity a) -> t m a
transreturn = toTrans
我确信我忽略了一些明显的东西。请为我指出来。
谢谢。
【问题讨论】:
-
State s a == StateT s Identity a,所以StateT s m a和m (StateT s Identity a)是两个不同的东西。一个与m (s -> (a, s))同构,另一个与s -> m (a, s)同构。 -
@chepner 这是一个公平的观点,但即使这是真的,它仍然没有解决我的高级问题,即:我假设如果一个 monad 有一个关联的转换器,那么任何你可以通过堆叠单子来做的事情,你可以通过使用变压器来做,称之为“变压器原理”。所以我想要做的是,你可以通过堆叠 monad 来做到这一点,正如我的实现
recFunc所展示的那样。因此,如果原理成立(并且由于Statemonad 确实有一个转换器),那么必须有一种方法可以使用转换器来做到这一点。那是什么? -
是的,但是您的转换不仅仅是堆叠单子。
recFuncT与recFunc不同。如果相同,则根本不需要更改正文,因为只需将State替换为等效的StateT只是别名扩展。 -
@chepner 我很确定你一定没有抓住重点。您始终可以将
State替换为StateT,但并非StateT的所有实例都可以直接替换为State的实例。也就是说,只要m不是身份,那么StateT s m a就不能用State重写。然而,从概念上和我理解的变形金刚,StateT s m a是“在m中插入State功能”,因此至少应该具有m (State s a)的所有功能。你是说这不是真的,有些事情你只能用m (State s a)做吗? -
我认为问题在于
StateT s m a可以被视为“在m中插入State功能”,但m (State s a)类型不能。甚至不清楚m (State s a)是 是什么,至少对于一般的monadm。IO (State s a)是什么?如果它应该是一个 monad,它的绑定运算符是什么样的?如果它不应该是一个单子,它的“能力”是什么?