Haskell 结合状态单子和共状态单子
如何将状态monadHaskell 结合状态单子和共状态单子,haskell,functional-programming,monads,category-theory,comonad,Haskell,Functional Programming,Monads,Category Theory,Comonad,如何将状态monadS->(A,S)与共状态monad(E->A,E)相结合 我尝试了两种明显的组合S->((E->A,E),S)和(E->S->(A,S),E),但在这两种情况下,我都不知道如何定义操作(返回,提取,…等等)对于组合。如果O或I两个单子同时指向,即使用提取方法,则组合两个单子O和I将产生一个单子。每个科摩纳德都是共同点。如果O和I`都是共点的,那么您有两种不同的“自然”方法来获得单子,这两种方法可能是不等价的 你有: unit_O :: a -> O a join_O :
S->(A,S)
与共状态monad(E->A,E)
相结合
我尝试了两种明显的组合
S->((E->A,E),S)
和(E->S->(A,S),E)
,但在这两种情况下,我都不知道如何定义操作(返回
,提取
,…等等)对于组合。如果O
或I
两个单子同时指向,即使用提取方法,则组合两个单子O
和I
将产生一个单子。每个科摩纳德都是共同点。如果O
和I`都是共点的,那么您有两种不同的“自然”方法来获得单子,这两种方法可能是不等价的
你有:
unit_O :: a -> O a
join_O :: O (O a) -> O a
unit_I :: a -> I a
join_I :: I (I a) -> I a
在这里,我添加了\u O
和\u I
后缀,以清晰明了;在实际的Haskell代码中,它们不会出现,因为类型检查器会自己解决这个问题
您的目标是显示O(io(ia))
是单子。让我们假设O
是共点的,即有一个函数extract\u O::O a->a
然后我们有:
unit :: a -> O (I a)
unit = unit_O . unit_I
join :: O (I (O (I a))) -> O (I a)
当然,问题在于实现join
。我们遵循这一战略:
fmap
在外部O
- 使用
extract\u O
获得内部O
- 使用
join_I
组合两个I
monad
这让我们想到
join = fmap_O $ join_I . fmap_I extract
要使其工作,还需要定义
newtype MCompose O I a = MCompose O (I a)
并将相应的类型构造函数和解构器添加到上述定义中
另一种选择是使用extract\u I
而不是extract\u O
。这个版本更简单:
join = join_O . fmap_O extract_I
这定义了一个新的单子。我假设你可以用同样的方式定义一个新的comonad,但我没有尝试过。正如另一个答案所示,这两个组合S->((E->a,E),S)
和(E->S->(a,S),E)
同时拥有Monad和Comonad实例。事实上,给出Monad/Comonad实例是
相当于给出一个
拟幺半群结构。其要点∀r、 r->f(r)或其同点∀r、 f(r)->r,至少在经典中,
非建设性的感觉(我不知道建设性的答案)。这一事实表明,实际上
函子f很有可能
如果它的点和余点是非平凡的,则它可以是单子和余子
然而,真正的问题是这样构造的Monad/Comonad实例是否具有自然属性
计算/范畴意义。在这种情况下,我会说“不”,因为你似乎没有
关于如何以适合您计算需要的方式组合它们的先验知识
合成两个(共)单体的标准分类方法是通过附加。让我总结一下你的情况:
Fₑ Fₛ
--> -->
Hask ⊣ Hask ⊣ Hask
<-- <--
Gₑ Gₛ
Fₜ(a) = (a,t)
Gₜ(a) = (t->a)
现在您可以看到状态monad(s->(a,s))≃ (s->a,s->s)是G的组成部分ₛFₛ 还有costate comonad
是F吗ₑGₑ. 这个附加说明说Hask可以被解释为(co)状态(co)代数的模型
现在,“附属品构成”例如
FₛFₑ(x) -> y ≃
Fₑ(x) -> Gₛ(y) ≃
x -> GₑGₛ(y)
所以FₛFₑ ⊣ GₑGₛ. 这就产生了一对单子和一对单子,即
T(a) = GₑGₛFₛFₑ(a)
= GₑGₛFₛ(a,e)
= GₑGₛ(a,e,s)
= Gₑ(s->(a,e,s))
= e->s->(a,e,s)
= ((e,s)->a, (e,s)->(e,s))
G(a) = FₛFₑGₑGₛ(a)
= FₛFₑGₑ(s->a)
= FₛFₑ(e->s->a)
= Fₛ(e->s->a,e)
= (e->s->a,e,s)
= ((e,s)->a, (e,s))
T是简单的状态单子和状态(e,s),G是共价态和共价态(e,s),所以
这些确实有非常自然的含义
撰写附加语是一种自然、频繁的数学运算。例如,一个几何态射
topoi(一种在“类型级别”上允许复杂(非自由)结构的笛卡尔闭范畴)被定义为一对附加,只要求其左伴随是左精确的(即保留有限极限)。如果这些拓扑是拓扑空间上的滑轮,
组成附加语仅仅对应于组成(唯一的)连续的基本变化图(在相反的方向),具有非常自然的意义
另一方面,在数学中,直接编写单子/单子似乎是一种非常罕见的实践。
这是因为通常(co)单子被认为是(co)代数理论的载体,而不是一个单子
模型在这种解释中,相应的附加语是模型,而不是单子。问题
组成两种理论需要另一种理论,一种关于如何组成它们的理论。例如
想象一下组成两个幺半群理论。那么你可能会得到至少两个新的理论,
即列表的列表理论,或两种二进制运算分布的环形代数。
两者都不比另一个更自然。这就是“单子不作曲”的意思;这并不是说构图不能是单子,而是说你需要另一种理论来构图
相比之下,撰写附加语自然会产生另一个附加语,因为这样做你就是一个附加语
隐含性指定了构成两个给定理论的规则。因此,通过使用复合附加语的单子,你得到了一个理论,它也规定了复合规则。你的意思是这应该是单子和复合词?对我来说似乎有点乐观,认为这应该是可能的…没有特别的理由相信(a)单子的成分是单子,(b)科摩纳的成分是科摩纳的,或者(c)任何类型都是单子和科摩纳的(注意,身份是,注意这是多么极端)。因此,期望单子和辅音的组合是单子或辅音,更不用说两者都是非常冒险的。“分配定律将适用于状态共状态与共状态相同的情况,不幸的是,这既不正确也没有帮助。@Bob,在不同的上下文中使用它们,因为它们都有什么好处?”?你的问题听起来像“即使我不能将它们结合起来,我如何编写一个同时使用Bool
和Int
的函数程序?”当你谈到单子和复数之间的分配规律时,你会问
T(a) = GₑGₛFₛFₑ(a)
= GₑGₛFₛ(a,e)
= GₑGₛ(a,e,s)
= Gₑ(s->(a,e,s))
= e->s->(a,e,s)
= ((e,s)->a, (e,s)->(e,s))
G(a) = FₛFₑGₑGₛ(a)
= FₛFₑGₑ(s->a)
= FₛFₑ(e->s->a)
= Fₛ(e->s->a,e)
= (e->s->a,e,s)
= ((e,s)->a, (e,s))