Haskell 任意单子与可遍历单子的组合是否总是单子?
如果我有两个单子Haskell 任意单子与可遍历单子的组合是否总是单子?,haskell,monads,Haskell,Monads,如果我有两个单子m和n,并且n是可遍历的,我是否一定要有一个复合m-over-n单子 更正式地说,以下是我的想法: 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
m
和n
,并且n
是可遍历的,我是否一定要有一个复合m
-over-n
单子
更正式地说,以下是我的想法:
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
import-Control.Monad
导入Data.Functor.Compose
预绑定::(单子m,单子n)=>
m(na)->(a->m(nb))->m(nb)
mnx`prebind`f=do nx>=f=Compose$do nmnx不,它并不总是单子。您需要额外的兼容性条件来关联两个单元组的单元组操作和分配定律sequence::n(ma)->m(na)
,如上所述
给出了一个不满足兼容性条件的示例,即
S=m=[]
,装置X->SX向[X]发送X
T=n=(>)Bool
,或等效的TX=X×X,装置X->TX将X发送到(X,X)
Wikipedia页面右下角的图表不会转换,因为合成图S->TS->ST将xs::[a]
发送到(xs,xs)
,然后发送到从xs
提取的所有对的笛卡尔积;而右侧地图S->ST将xs
发送到“对角线”,该对角线仅由xs
中x
的(x,x)
对组成。正是这个问题导致你提出的单子不能满足其中一个单位定律。再补充几句,让它和你的具体问题之间的联系更加明确
在这种情况下,根据join
,计算出您的Monad
实例是值得的:
join' :: m (n (m (n b))) -> m (n b)
join' = fmap join . join . fmap sequence
通过在适当的位置重新引入compose
/getCompose
,并使用m>=f=join(fmap f m)
,您可以验证这确实等同于您的定义(请注意,您的预绑定
相当于该等式中的fmap f
)
这一定义使得用图表1来验证这些定律变得容易。这里有一个用于join。return=id
即(fmap join.join.fmap序列)。(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
。。。正如里德·巴顿(Reid Barton)的回答所显示的那样,这并不是一个确定的事实
如果我们对连接应用相同的策略。fmap return=id
law,右上角的图表,sequence。fmap return=return
,显示了这一点——然而,这本身并不是问题,因为这只是可遍历的的身份法则的直接结果。最后,对连接执行相同的操作。fmap join=join。join
law生成另外两个图--序列。fmap join=join。fmap序列。顺序
和顺序。join=fmap join。序列fmap序列
——弹出来
脚注:
简写图例:r
是return
,s
是sequence
,j
是join
。函数缩写后的大写字母和数字消除了相关单子的歧义,其引入或更改层的位置最终位于-,在s
的情况下,它指的是最初的内层,在这种情况下,我们知道外层始终是T
。层从下到上编号,从零开始。在第一个函数的下面写下第二个函数的速记来表示合成
是一本伟大的书,从范畴理论的角度(特别是p257“分配定律”)阐述了这一主题,并(从已经了解范畴理论的人的角度)对M的必要条件给出了相对简单的证明。如果M
和N
为单子,则N
为单子。这是另一个问题,它对您给出的代码有一个细微的变化-也许这是一个更有用的起点。扰流板:是的。如果回顾起来很明显,那么有点悲哀,以这种方式在列表上分层的状态不会给您带来不确定的状态,与将StateT分层到列表上时发生的情况相反。如果Traversable n
是实现此功能的完美约束,那么如果Data.Functor.Compose
拥有该实例就更好了。也许你该建议一下?我肯定能想到一个可以使用它的上下文。是的,这就是为什么我开始说“如果可遍历的n是完美的约束…”也许这有点模糊。当然,我们不需要一个以上的例子,只要最一般(正确)的一个。我想我遗漏了一些明显的东西。由于[]
是可遍历的,但(>)r
不是,因此上面的方法将提供一种在列表(或集合)单子上派生读卡器的方法,而不是在读卡器单子上派生列表的方法,这正是我前面的问题。对不起,我现在明白了为什么(>)Bool
确实是可遍历的。对于任何有限的r
(沿着您链接到的问题中暗示的行)(>)Bool
,或者对于任何有限类型r
,都是可遍历的,因为它相当于|r |
-元组。快速跟进。你在维基文章中链接的if是文字if(分布定律的存在是复合函子为一元函数的充分条件)还是数学家if(充分和必要)?我检查了右下角的图表是否需要ST以指定的方式成为一元函数(它遵循s、T和ST的单位定律). 我猜其他的图也可以以类似的方式被证明是必要的,因为它们也是从态射到ST之间的方程,但我看不出有什么特别的理由先验地认为它们是必要的。
M id M
---->
rM1 | | id
V V
---->
MM jM0 M
M id M
---->
rT1 | | rT0
V V
---->
TM sM1 MT