具有遍历的任意monad的构成是否始终为monad?

时间:2017-02-16 21:21:41

标签: haskell monads

如果我有两个monad mn,并且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上。

2 个答案:

答案 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 . joinsequence . fmap join = join . fmap sequence . sequence - 出现。

脚注:

  1. 简写的图例:sequence . join = fmap join . sequence . fmap sequencerreturnssequencej。函数缩写后的大写字母和数字消除了所涉及的monad的歧义,其引入或更改的层的位置最终在 - 在join的情况下,指的是< em>最初内层,因为在这种情况下我们知道外层总是s。这些层从零开始从下到上编号。通过在第一个函数下面写下第二个函数的速记来表示合成。