如果我有两个monad m
和n
,并且n
可以遍历,我是否必须有一个复合m
- over - n
monad?
更正式地说,这就是我的想法:
import Control.Monad
import Data.Functor.Compose
prebind :: (Monad m, Monad n) =>
m (n a) -> (a -> m (n b)) -> m (n (m (n b)))
mnx `prebind` f = do nx <- mnx
return $ do x <- nx
return $ f x
instance (Monad m, Monad n, Traversable n) => Monad (Compose m n) where
return = Compose . return . return
Compose mnmnx >>= f = Compose $ do nmnx <- mnmnx `prebind` (getCompose . f)
nnx <- sequence nmnx
return $ join nnx
当然,这种类型检查,我认为适用于我检查过的一些案例(Reader over List,State over List) - 如同组成的monad&#39;满足monad法则 - 但我不确定这是否是一个通用配方,用于将任何monad分层到可遍历的monad上。
答案 0 :(得分:7)
不,它并不总是一个单子。您需要与两个monad的monad操作和分配法sequence :: n (m a) -> m (n a)
相关的额外兼容性条件,例如在Wikipedia上所述。
Your previous question给出了一个不符合兼容性条件的例子,即
S = m = []
,单位为X - &gt; SX将x发送到[x];
T = n = (->) Bool
,或等效TX = X×X,单位X - > TX将x发送到(x,x)。
维基百科页面上的右下图不通勤,因为组合S - &gt; TS - &gt; ST将xs :: [a]
发送到(xs,xs)
,然后发送到从xs
中抽取的所有对的笛卡尔积;而右手地图S - &gt; ST将xs
发送到“对角线”,该对角线仅由(x,x)
中x
的{{1}}对组成。同样的问题导致你提议的monad不满足单位法之一。
答案 1 :(得分:4)
一些额外的评论,以使Reid Barton's general answer与您的具体问题之间的联系更加明确。
在这种情况下,根据Monad
计算join
个实例真的是值得的:
join' :: m (n (m (n b))) -> m (n b)
join' = fmap join . join . fmap sequence
通过在适当的位置重新引入compose
/ getCompose
并使用m >>= f = join (fmap f m)
,您可以验证这确实与您的定义相同(请注意,您的prebind
相当于该等式中的fmap f
。
这个定义使得用图表 1 验证法则变得很舒服。这是join . return = id
的一个,即(fmap join . join . fmap sequence) . (return . return) = id
:
3210 MT id MT id MT id MT ----> ----> ----> rT2 | | rT1 | | rT1 | | id rM3 V V rM3 V V V V ----> ----> ----> MTMT sM2 MMTT jM2 MTT jT0 MT
整体矩形是monad法则:
M id M ----> rM1 | | id V V ----> MM jM0 M
忽略在正方形两个方向上必然相同的部分,我们看到最右边的两个正方形符合相同的规律。 (将这些&#34; square&#34;和#34;矩形&#34;称为它们所有id
侧面当然有点愚蠢,但它更符合我有限的ASCII艺术技巧。)然而,第一个方格相当于sequence . return = fmap return
,这是维基百科页面中的右下图Reid Barton提到的......
M id M ----> rT1 | | rT0 V V ----> TM sM1 MT
......正如Reid Barton的回答所显示的那样,并不是一个特定的。
如果我们将相同的策略应用于join . fmap return = id
法则,右上图sequence . fmap return = return
会出现 - 然而,这本身并不是问题,因为这只是({1}}身份法的直接后果。最后,使用Traversable
定律做同样的事情会使另外两个图表 - join . fmap join = join . join
和sequence . fmap join = join . fmap sequence . sequence
- 出现。
脚注:
sequence . join = fmap join . sequence . fmap sequence
为r
,return
为s
,sequence
为j
。函数缩写后的大写字母和数字消除了所涉及的monad的歧义,其引入或更改的层的位置最终在 - 在join
的情况下,指的是< em>最初内层,因为在这种情况下我们知道外层总是s
。这些层从零开始从下到上编号。通过在第一个函数下面写下第二个函数的速记来表示合成。