如何将 state monadS -> (A, S)
与 costate comonad结合起来(E->A, E)
?
我尝试了两种明显的组合S -> ((E->A, E), S)
,(E->S->(A, S), E)
但是在任何一种情况下,我都不知道如何为组合定义操作(return
, extract
, ... 等等)。
如何将 state monadS -> (A, S)
与 costate comonad结合起来(E->A, E)
?
我尝试了两种明显的组合S -> ((E->A, E), S)
,(E->S->(A, S), E)
但是在任何一种情况下,我都不知道如何为组合定义操作(return
, extract
, ... 等等)。
结合两个 monadO
并I
产生一个 monad,如果其中一个O
或I
是共指的,即有一个extract
方法。每个comonad是copointed。如果两者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
为了清楚起见,我在这里添加了_O
后缀_I
;在实际的 Haskell 代码中,它们不会存在,因为类型检查器会自行计算出来。
你的目标是证明这O (I O (I a)))
是一个单子。让我们假设它O
是共指的,即有一个函数extract_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_O
搭便车O
join_I
结合两个I
monad这导致我们
join = fmap_O $ join_I . fmap_I extract
为了使这项工作,您还需要定义
newtype MCompose O I a = MCompose O (I a)
并将各自的类型构造函数和解构函数添加到上面的定义中。
另一种替代方法是使用extract_I
而不是extract_O
. 这个版本更简单:
join = join_O . fmap_O extract_I
这定义了一个新的 monad。我假设您可以以相同的方式定义一个新的共单,但我没有尝试过。
正如另一个答案所示,这两种组合都S -> ((E->A, E), S)
同时(E->S->(A, S), E)
具有 Monad 和 Comonad 实例。事实上,给一个 Monad/Comonad 实例相当于给一个 monoid 结构。它的点∀rr->f(r) 或它的共点∀rf (r)->r,至少在经典的、非建设性的意义上(我不知道建设性的答案)。这一事实表明,实际上 Functor f 很有可能既可以是 Monad 又可以是 Comonad,只要它的点和共点是非平凡的。
然而,真正的问题是这样构造的 Monad/Comonad 实例是否具有自然的计算/分类意义。在这种特殊情况下,我会说“不”,因为您似乎没有 关于如何以适合您的计算需求的方式组合它们的先验知识。
组成两个(共)单子的标准分类方法是通过附加词。让我总结一下你的情况:
Fₑ Fₛ
--> -->
Hask ⊣ Hask ⊣ Hask
<-- <--
Gₑ Gₛ
Fₜ(a) = (a,t)
Gₜ(a) = (t->a)
Fₜ ⊣ Gₜ 的证明:
Fₜ(x) -> y ≃
(x,t) -> y ≃
x -> (t->y) ≃
x -> Gₜ(y)
现在你可以看到状态单子 (s->(a,s)) ≃ (s->a,s->s) 是合成 GₛFₛ 而并列共子是 FₑGₑ。这个附加词说 Hask 可以解释为(共)态(共)代数的模型。
现在,“附加组成”。例如,
FₛFₑ(x) -> y ≃
Fₑ(x) -> Gₛ(y) ≃
x -> GₑGₛ(y)
所以 FₛFₑ ⊣ GₑGₛ。这给出了一对monad和comonad,即
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(一种允许在“类型级别”上进行复杂(非自由)构造的笛卡尔封闭类别)之间的几何态射被定义为一对附属物,只要求其左附属物保持精确(即保留有限的限制)。如果这些topoi 是拓扑空间上的滑轮,那么组成附属词只对应于组成(唯一的)连续碱基变化图(在相反方向上),具有非常自然的意义。
另一方面,直接组合单子/comonads 似乎是数学中非常罕见的实践。这是因为(共)单子通常被认为是(共)代数理论的载体,而不是模型。在这种解释中,相应的附加词是模型,而不是单子。问题是组合两个理论需要另一个理论,一个关于如何组合它们的理论。例如,想象组成两个幺半群理论。然后你可能会得到至少两个新的理论,即列表的列表理论,或两种二元运算分布的环状代数。也不是先验的比其他更好/更自然。这就是“monads don't compose”的意思;它并不是说组合不能是单子,但它确实说你需要另一种理论来组合它们。
相反,组合附加语自然会导致另一个附加语,因为这样做是在隐含地指定组合两个给定理论的规则。因此,通过采用组合附加词的单子,您可以获得同时指定组合规则的理论。