我一直想知道为什么 Monad Transformers 有例如 m(Maybe(m(Maybe a))
结构顺序和为什么不Maybe(m(Maybe( m a)))
.我试图实现第二个,但我无法完成它可能是因为我对 Haskell 知识不足。
在所有 monad 转换器中,我们总是有这样的结构吗?
如果是,那为什么?
如果不是那么什么时候从另一个中选择一个?
newtype MaybeOT m a = MaybeOT {runMaybeOT :: Maybe (m a) }
instance (Monad m) => Monad (MaybeOT m) where
return = pure
(MaybeOT mma) >>= f = MaybeOT $
case mma of
Nothing -> Nothing
(Just ma) -> inner ma where
inner ma = do
a <- ma
undefined
最佳答案
您基本上已经发现为什么 monad 转换器是向内的而不是向外的(尼特:我很确定我在这里没有使用正确的术语,但您明白我的意思 - 在这里随时纠正我)。将您所知道的 monad 放在 中要容易得多(简单 == 更少约束)。数据位置 (内部),而不是 容器位置 (外部)。直观地,您可以理解为什么 - 毕竟,一元性质需要 容器是一样的。你可以对数据做任何你想做的事情,但你不能在绑定(bind)期间改变容器的类型。
当然,通过实际尝试实现 absurd ,更容易实现这一切。这是关于什么的。您在实现 >>=
时会遇到的最后一步/bind
为 MaybeOT
这是-
m a >>= ??? . runMaybeOT . f
哪里,m
是 Monad
, m a :: m a
, f :: a -> MaybeOT m a
, 和 runMaybeOT :: MaybeOT m a -> Maybe (m a)
(还有 ??? :: ???
,呵呵)现在,您必须获得容器类型为
m
的 monad成功绑定(bind) m a
.但可惜,您无法获得 m a
出Maybe (m a)
!当它是 Nothing
时会发生什么?您实现 monad 转换器的主要工具是有关 的知识。特定的 monad 你正在实现变压器。而你的知识告诉你这是 absurd 的。相比之下,实现
>>=
的最后一步为 MaybeT
进展顺利。为了完整起见,这里是 MaybeT
——newtype MaybeT m a = MaybeT { runMaybeT :: m (Maybe a) }
在这里,您将绑定(bind)类型 m (Maybe a)
.在绑定(bind)期间,您必须返回类型 m b
,不管 b
.你得到一个函数,f
, 类型 a -> MaybeT m b
,您可以轻松获得m (Maybe b)
来自 MaybeT m b
.啊哈!现在您可以看到解决方案。您获得的函数返回与您绑定(bind)的那个相同的外部 monad。这正是您所需要的。在
MaybeOT
的情况下,你被困在 monad 上 m
, 带有一个不返回带有外部 monad 的值的函数 m
!这就是关键的实现——你得到的函数必须能够用相同的外部 monad 给你一个值。这就是为什么
MaybeT
(以及其他转换器)将未知的 monad 保持在外面 - 因为当你实现 >>=
时为 MaybeT
,您知道构造该外部 monad 需要绑定(bind)函数。至少在直觉上,注意到您在这里遇到的问题是有帮助的 - 与您在实现 monadic 组合时将面临的问题完全相同。即
>>=
对于嵌套的 monad。 Monads don't compose !这就是为什么你需要 monad 转换器!如果你分解这个问题,你会注意到如果你有一个可以交换 monad 的函数,得到
Maybe (m a)
来自 m (Maybe a)
反之亦然——一切都会好起来的。 Monad 可以组合,Monad Transformer 可以看起来像你喜欢的样子,事实上,Monad Transformer 基本上没有任何用处。这个细节被注意到in the answer linked above .所有你需要的是-swap :: (Monad m, Monad n) => m (n a) -> n (m a)
This exists .它们被称为可遍历的单子(monad),事实上,如果你仅仅添加 Traversable
限制您的 Monad
MaybeOT
的实现,你中了金——instance (Traversable m, Monad m) => Monad (MaybeOT m) where
return = pure
MaybeOT Nothing >>= _ = MaybeOT Nothing
MaybeOT (Just ma) >>= f = MaybeOT $ sequence $ ma >>= sequence . runMaybeOT . f
我认为它是符合法律的,但我必须检查一下。在另一个注意事项中,完全有可能使
MaybeOT
符合适用的法律。毕竟,您在实现 Monad
时面临的问题在这里根本不存在。应用程序确实构成。instance Applicative f => Applicative (MaybeT f) where
pure = MaybeT . pure . Just
MaybeT f <*> MaybeT a = MaybeT $ liftA2 (<*>) f a
(或者,也许更简单)instance Applicative f => Applicative (MaybeT f) where
pure = MaybeT . pure . Just
MaybeT f <*> MaybeT a = MaybeT $ (<*>) <$> f <*> a
据我所知,这应该是符合法律的。
关于haskell - Monad Transformer 内部结构顺序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69864664/