这里的答案在定义幺半群和单子方面做得很好,但是,他们似乎仍然没有回答这个问题:
还有一个不太重要的说明,这是真的吗?如果是的话,您能否给出解释(希望没有太多 Haskell 经验的人可以理解)?
这里缺少的问题的症结在于“monoid”的不同概念,更准确地说是所谓的分类 - monoidal 类别中的幺半群。可悲的是 Mac Lane 的书本身makes it very confusing:
总而言之,X 中的 monad 只是 X endofunctors 类别中的一个幺半群,产品 × 被 endofunctors 的组合替换,单位由身份 endofunctor 设置。
主要混淆
为什么会令人困惑?因为它没有定义X 的“内函数类中的monoid”是什么。相反,这句话建议在所有内函子的集合中取一个幺半群,将函子组合作为二元运算,将恒等函子作为一个幺半群单元。它工作得非常好,并变成一个幺半群,包含恒等函子并在函子组合下封闭的内函子的任何子集。
然而,这不是正确的解释,这本书在那个阶段没有明确说明。 Monad f 是一个固定 endofunctor,而不是在组合下封闭的endofunctor 的子集。一种常见的构造是使用f 来生成一个幺半群,方法是使用f 的所有f^k = f(f(...)) 的集合,包括对应于身份f^0 = id。现在,所有k>=0 的所有这些权力的集合S 确实是一个幺半群,“其中乘积×被内函子的组合取代,单位由恒等函子设置”。
然而:
- 这个幺半群
S 可以定义为任何函子f,甚至可以定义为X 的任何自映射。是f生成的幺半群。
- 由函子组合和恒等函子给出的
S 的幺半群结构与 f 是否是单子无关。
为了让事情更混乱,“幺半群中的幺半群”的定义出现在本书后面,您可以从table of contents 中看到。然而,理解这个概念对于理解与 monad 的联系是绝对关键的。
(严格)幺半群类别
转到关于 Monoids 的第 VII 章(晚于关于 Monads 的第 VI 章),我们发现所谓的严格 monoidal 类别的定义为三元组(B, *, e),其中B 是一个类别,*: B x B-> B一个bifunctor(对于每个组件的函子,其他组件固定),e是B中的一个单位对象,满足关联性和单位定律:
(a * b) * c = a * (b * c)
a * e = e * a = a
对于B 的任何对象a,b,c,以及e 替换为id_e 的任何态射a,b,c 的相同恒等式,e 的恒等态射。现在观察到,在我们感兴趣的情况下,B 是具有自然变换作为态射的 X 的内函子类别,* 函子组合和 e 恒等函子,所有这些定律都是满意,可以直接验证。
本书后面是“松弛”monoidal category的定义,其中定律只对满足所谓相干关系的一些固定自然变换进行模数运算 ,但这对于我们的内函子类别并不重要。
幺半群类别中的幺半群
最后,在第七章的第 3 节“Monoids”中给出了实际的定义:
幺半群c 在幺半群类别(B, *, e) 是B 的对象,带有两个箭头(态射)
mu: c * c -> c
nu: e -> c
使 3 个图可交换。回想一下,在我们的例子中,这些是内函子范畴中的态射,它们是精确对应于单子的join 和return 的自然变换。当我们使组合 * 更加明确时,这种联系变得更加清晰,将 c * c 替换为 c^2,其中 c 是我们的 monad。
最后,请注意 3 个交换图(在幺半群中的幺半群的定义中)是为一般(非严格)幺半群类别编写的,而在我们的例子中,作为幺半群的一部分产生的所有自然变换实际上都是身份。这将使图表与 monad 定义中的图表完全相同,从而使对应关系完整。
结论
总之,根据定义,任何 monad 都是 endofunctor,因此是 endofunctor 类别中的一个对象,其中 monadic join 和 return 运算符满足该特定(严格)monoidal 中的幺半群的定义类别。反之亦然,根据定义,内函子类中的任何幺半群都是由一个对象和两个箭头组成的三元组(c, mu, nu),例如在我们的例子中是自然变换,满足与单子相同的规律。
最后,请注意(经典)幺半群与幺半群类别中更一般的幺半群之间的主要区别。上面的两个箭头mu 和nu 不再是二元运算和集合中的一个单元。相反,您有一个固定的内函子c。函子组合 * 和恒等函子本身并不能提供 monad 所需的完整结构,尽管书中有令人困惑的评论。
另一种方法是与集合A 的所有自映射的标准幺半群C 进行比较,其中二元运算是组合,可以看出将标准笛卡尔积C x C 映射到C。传递给分类的幺半群,我们将笛卡尔积 x 替换为函子组合 *,二元运算被替换为自然变换 mu 来自
c * c 到 c,这是 join 运算符的集合
join: c(c(T))->c(T)
对于每个对象T(输入编程)。而经典幺半群中的恒等元素,可以用来自固定单点集的地图图像来识别,被return 运算符的集合替换
return: T->c(T)
但是现在没有更多的笛卡尔积,所以没有元素对,因此也没有二元运算。