探索这种材料:Lens over tea我遇到了一个有趣的(起初很简单)的观点:
ex3 :: (a, b) -> (b, a)
ex3 = do
a <- fst
b <- snd
return (b, a)
一切都很好,但是此函数使用哪种类型的monad(因为我们内部有一个do-block)。经过几次尝试,我得出了以下结论:
ex2 :: ReaderT (a, b) ((,) b) a
ex2 = ReaderT $ do
a <- fst
b <- snd
return (b, a)
ex3 :: (a, b) -> (b, a)
ex3 = runReaderT ex2
因此,我们有使用内部monad((,)b)的ReaderT。有趣的是-我对此不太满意,因此决定不使用do-notation重写ex2。这就是我得到的:
ex2 :: Monoid b => ReaderT (a, b) ((,) b) a
ex2 = ReaderT $
\pair -> return (fst pair) >>=
\a -> return (snd pair) >>=
\b -> (b, a)
甚至:
ex2 :: Monoid b => ReaderT (a, b) ((,) b) a
ex2 = ReaderT $
\pair -> (mempty, fst pair) >>=
\a -> (mempty, snd pair) >>=
\b -> (b, a)
两个变体都要求b具有Monoid类型限制。 问题是:是否可以仅使用(>> =)而不使用Monoid限制来编写此函数-就像我们使用do-notation变体一样?显然,无论有无注释,我们都可以这样做。也许甚至是不同的是,我们必须在第二和第三功能的每一步都构造单子,这要求我们声明“ b”应该是一个单半体-一些单半体。在第一种情况下,我们只是从某个单子中提取我们的值,而不是构建它们。谁能解释我在朝着正确的方向思考吗?
谢谢!
答案 0 :(得分:2)
您尚未完全将其从do
表示法转换为(>>=)
调用。直接翻译如下:
ex2 :: ReaderT (a, b) ((,) b) a
ex2 = ReaderT $
fst >>= (\a -> -- a <- fst
snd >>= (\b -> -- b <- snd
return (b, a))) -- return (b, a)
此外,即使它适合(,) b
的“内部monad”插槽,您实际上并没有使用ReaderT
的monadness。
答案 1 :(得分:1)
ex3 :: (a, b) -> (b, a)
是指前缀符号
ex3 :: (->) (a, b) (b, a)
-----------m
------t
因此,单子为m = (->) (a, b)
,它是Reader对子(直到同构),其对作为其隐式参数/只读状态。
您不需要Monoid。普通读者monad就足够了。如果要使用ReaderT
,请使用标识monad作为内部monad。
ex2 :: Monoid b => ReaderT (a, b) Identity (b, a)
ex2 = ReaderT $
\pair -> Identity (fst pair) >>=
\a -> Identity (snd pair) >>=
\b -> Identity (b, a)
当然,上面的代码可以简化。
答案 2 :(得分:0)
所以总结一下:
如何不执行初始功能就对糖进行去糖处理:
ex3' :: (a, b) -> (b, a)
ex3' = fst >>=
\a -> snd >>=
\b -> return (b, a)