haskell - Monad Transformer 内部结构顺序

标签 haskell monads monad-transformers

我一直想知道为什么 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 ,更容易实现这一切。这是关于什么的。您在实现 >>= 时会遇到的最后一步/bindMaybeOT这是-

m a >>= ??? . runMaybeOT . f
哪里,mMonad , m a :: m a , f :: a -> MaybeOT m a , 和 runMaybeOT :: MaybeOT m a -> Maybe (m a) (还有 ??? :: ??? ,呵呵)
现在,您必须获得容器类型为 m 的 monad成功绑定(bind) m a .但可惜,您无法获得 m aMaybe (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/

相关文章:

haskell - 如何将 QuickCheck 参数限制为非空字符串列表?

haskell - Haskell 将 Hom Functor/Monad 称为什么?

haskell - 列出单子(monad)转换器

haskell - StateT 超过 Cont。为什么我的状态没有被重置?

performance - 当它们应该是相同的东西时,为什么 >>= 比 concatMap 快?

Haskell -- 如何将数字拆分为列表以进行进一步处理?

haskell - GHC 9 中量化约束的行为变化

haskell - 在 Writer monad 中交换 `mappend`

haskell - 在运行的 haskell 程序中生成输出

f# - 如何在 F# 中组合状态和延续单子(monad)