单子只是内函子类别中的幺半群,有什么问题吗?
下面这句话是谁先说的?
单子只是其中的一个幺半群 内函子的类别,是什么 有问题吗?
另一个不太重要的问题是,这是真的吗?如果是的话,您能否给出一个解释(希望没有太多 Haskell 经验的人能够理解)?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
下面这句话是谁先说的?
单子只是其中的一个幺半群 内函子的类别,是什么 有问题吗?
另一个不太重要的问题是,这是真的吗?如果是的话,您能否给出一个解释(希望没有太多 Haskell 经验的人能够理解)?
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
接受
或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
发布评论
评论(6)
这个特殊的措辞是 James Iry 的,来自他非常有趣的编程语言的简短、不完整且大多错误的历史,其中他虚构地将其归因于 Philip Wadler。
原始引用来自 Saunders Mac Lane 的《工作数学家的范畴》,范畴论的基础文本之一。 这里是上下文,这可能是了解其确切含义的最佳位置。
不过,我还是会尝试一下。原句是这样的:
X 这里是一个类别。 Endofunctor 是从一个类别到其自身的函子(就函数式程序员而言,通常是所有
函子
,因为他们大多只处理一个类别;类别类型 - 但我离题了)。但你可以想象另一个类别,即“X 上的endofunctors”类别。在这个范畴中,对象是内函子,态射是自然变换。在这些内函子中,其中一些可能是单子。哪些是单子?正是那些在特定意义上幺半群。我不会详细说明从 monad 到 monoid 的精确映射(因为 Mac Lane 在这方面做得比我希望的要好得多),我只是将它们各自的定义并排放置并让您进行比较:
幺半群是...
...满足以下定律:
单子是...
Functor
实例的* -> *
类型构造函数)join
(Haskell 中)return
在 Haskell 中)...满足这些定律:
稍微眯一下眼睛,您可能会发现这两个定义都是相同的实例 抽象概念。
That particular phrasing is by James Iry, from his highly entertaining Brief, Incomplete and Mostly Wrong History of Programming Languages, in which he fictionally attributes it to Philip Wadler.
The original quote is from Saunders Mac Lane in Categories for the Working Mathematician, one of the foundational texts of Category Theory. Here it is in context, which is probably the best place to learn exactly what it means.
But, I'll take a stab. The original sentence is this:
X here is a category. Endofunctors are functors from a category to itself (which is usually all
Functor
s as far as functional programmers are concerned, since they're mostly dealing with just one category; the category of types - but I digress). But you could imagine another category which is the category of "endofunctors on X". This is a category in which the objects are endofunctors and the morphisms are natural transformations.And of those endofunctors, some of them might be monads. Which ones are monads? Exactly the ones which are monoidal in a particular sense. Instead of spelling out the exact mapping from monads to monoids (since Mac Lane does that far better than I could hope to), I'll just put their respective definitions side by side and let you compare:
A monoid is...
...satisfying these laws:
A monad is...
* -> *
with aFunctor
instance)join
in Haskell)return
in Haskell)...satisfying these laws:
With a bit of squinting you might be able to see that both of these definitions are instances of the same abstract concept.
首先,我们将要使用的扩展和库:
其中,
RankNTypes
是唯一对以下内容绝对重要的扩展和库。 我曾经写过一篇关于 RankNTypes 的解释人们似乎发现有用,所以我会参考一下。引用 Tom Crockett 的出色回答,我们有:
我们如何将其转换为哈斯克尔代码?好吧,让我们从自然变换的概念开始:
f :->; 形式的类型。 g
类似于函数类型,但不要将其视为两个类型(类型为*
)之间的函数 ,将其视为两个函子(每种* -> *
)之间的态射。示例:基本上,在 Haskell 中,自然转换是从某种类型
f x
到另一种类型g x
的函数,使得x
类型变量“不可访问”给来电者。例如,sort :: Ord a => [一]-> [a]
无法进行自然转换,因为它对我们可以为a
实例化的类型“挑剔”。我经常用来思考这个问题的一种直观方式如下:现在,解决这个问题,让我们来处理定义的子句。
第一个子句是“一个endofunctor,T : X -> X”。好吧,Haskell 中的每个函子都是人们所谓的“Hask 类别”中的一个内函子,其对象是 Haskell 类型(类型为
*
),其态射是 Haskell 函数。这听起来像是一个复杂的说法,但实际上是一个非常简单的说法。它的意思是函子 f :: * -> * 为您提供了为任何a :: *
构造类型fa :: *
和函数fmap f :: fa -> 的方法; f b 出自任何
f :: a ->; b
,并且它们遵守函子定律。第二个子句:Haskell 中的
Identity
函子(平台附带,因此您可以导入它)是这样定义的:所以自然变换η : I ->对于任何
Monad
实例t
,Tom Crockett 定义中的 T 可以这样写:第三个子句:Haskell 中两个函子的组合可以是如此定义(平台也附带):
因此自然变换μ : T × T -> Tom Crockett 的定义中的 T 可以这样写:
这是 endofunctors 类别中的幺半群的陈述意味着
Compose
(部分应用于其前两个参数)是关联的,Identity
是它的标识元素。即,以下同构成立:Compose f (Compose gh) ~= Compose (Compose fg) h
Compose f Identity ~= f
Compose Identity g ~= g
这些很容易证明,因为
Compose
和Identity
都定义为newtype
,并且 Haskell Reports 定义了以下语义newtype
作为所定义的类型与newtype
数据构造函数的参数类型之间的同构。例如,让我们证明Compose f Identity ~= f
:First, the extensions and libraries that we're going to use:
Of these,
RankNTypes
is the only one that's absolutely essential to the below. I once wrote an explanation ofRankNTypes
that some people seem to have found useful, so I'll refer to that.Quoting Tom Crockett's excellent answer, we have:
How do we translate this to Haskell code? Well, let's start with the notion of a natural transformation:
A type of the form
f :-> g
is analogous to a function type, but instead of thinking of it as a function between two types (of kind*
), think of it as a morphism between two functors (each of kind* -> *
). Examples:Basically, in Haskell, natural transformations are functions from some type
f x
to another typeg x
such that thex
type variable is "inaccessible" to the caller. So for example,sort :: Ord a => [a] -> [a]
cannot be made into a natural transformation, because it's "picky" about which types we may instantiate fora
. One intuitive way I often use to think of this is the following:Now, with that out of the way, let's tackle the clauses of the definition.
The first clause is "an endofunctor, T : X -> X." Well, every
Functor
in Haskell is an endofunctor in what people call "the Hask category," whose objects are Haskell types (of kind*
) and whose morphisms are Haskell functions. This sounds like a complicated statement, but it's actually a very trivial one. All it means is that that aFunctor f :: * -> *
gives you the means of constructing a typef a :: *
for anya :: *
and a functionfmap f :: f a -> f b
out of anyf :: a -> b
, and that these obey the functor laws.Second clause: the
Identity
functor in Haskell (which comes with the Platform, so you can just import it) is defined this way:So the natural transformation η : I -> T from Tom Crockett's definition can be written this way for any
Monad
instancet
:Third clause: The composition of two functors in Haskell can be defined this way (which also comes with the Platform):
So the natural transformation μ : T × T -> T from Tom Crockett's definition can be written like this:
The statement that this is a monoid in the category of endofunctors then means that
Compose
(partially applied to just its first two parameters) is associative, and thatIdentity
is its identity element. I.e., that the following isomorphisms hold:Compose f (Compose g h) ~= Compose (Compose f g) h
Compose f Identity ~= f
Compose Identity g ~= g
These are very easy to prove because
Compose
andIdentity
are both defined asnewtype
, and the Haskell Reports define the semantics ofnewtype
as an isomorphism between the type being defined and the type of the argument to thenewtype
's data constructor. So for example, let's proveCompose f Identity ~= f
:这里的答案在定义幺半群和单子方面做得很好,但是,它们似乎仍然没有回答这个问题:
这里缺少的问题的关键是“幺半群”的不同概念,更准确地说是所谓的分类——幺半群在幺半群范畴中的概念。遗憾的是 Mac Lane 的书本身 让人非常困惑:
主要困惑
为什么这会令人困惑?因为它没有定义什么是
X
的“endofunctors范畴中的幺半群”。相反,这句话建议将所有内函子集合内的幺半群与函子组合一起作为二元运算,将恒等函子作为幺半群单元。它工作得很好,并且将包含恒等函子并在函子组合下封闭的内函子的任何子集变成幺半群。但这并不是正确的解释,本书当时未能明确说明这一点。 Monad
f
是一个固定的endofunctor,而不是组合下封闭的endofunctor的子集。常见的构造是使用f
通过获取所有k
折叠组合的集合来生成一个幺半群f^k = f
与其自身,包括对应于身份f
的 (f(...))f^0 = id
k=0代码>.现在,所有k>=0
的所有这些幂的集合S
确实是一个幺半群,“乘积 × 替换为 endofunctor 的组合,单位由恒等 endofunctor 设置” 。然而:
S
可以为任何函子f
定义,甚至可以为X
的任何自映射定义。它是由f
生成的幺半群。S
幺半群结构与f
是否是单子无关。更令人困惑的是,“幺半群类别中的幺半群”的定义在本书后面出现,您可以从 目录。然而,理解这个概念对于理解与单子的联系绝对至关重要。
(严格)幺半群范畴
转到关于幺半群的第七章(比关于单子的第六章晚),我们发现所谓的严格幺半群范畴的定义为三重
(B, * , e)
,其中B
是类别,*: B x B-> B
是一个 bifunctor(相对于每个组件的函子,其他组件固定),e
是B
中的一个单位对象,满足结合律和单位定律:对于
B
的任何对象a,b,c
,以及对于任何态射a,b,c
具有相同的恒等式将e
替换为id_e
,即e
的恒等态射。现在观察一下,在我们感兴趣的例子中,其中B
是X
的内函子类别,自然变换为态射,*
函子复合和e
恒等函子,所有这些定律都满足,可以直接验证。书中接下来是“宽松”幺半群范畴的定义,其中定律仅对某些满足所谓相干关系的固定自然变换进行取模,即然而对于我们的内函子类别的例子来说并不重要。
:
最后,在第七章第 3 节“幺半群”中,给出了实际的定义
使 3 个图可交换。回想一下,在我们的例子中,这些是 endofunctors 类别中的态射,它们是与 monad 的
join
和return
精确对应的自然变换。当我们使组合*
更加明确时,用c^2
替换c * c
,其中c< /code> 是我们的 monad。
最后,请注意,3 个交换图(在幺半群范畴中的幺半群定义中)是为一般(非严格)幺半群范畴编写的,而在我们的例子中,作为幺半群范畴的一部分而出现的所有自然变换实际上都是恒等式。这将使图表与单子定义中的图表完全相同,从而使对应关系完整。
结论
总之,任何 monad 根据定义都是一个 endofunctor,因此是 endofunctor 类别中的对象,其中 monadic
join
和return
运算符满足 a 的定义特定(严格)幺半群类别中的幺半群。反之亦然,根据定义,内函子幺半群类别中的任何幺半群都是由一个对象和两个箭头组成的三元组(c, mu, nu)
,例如我们例子中的自然变换,满足与一个单子。最后,请注意(经典)幺半群和幺半群类别中更一般的幺半群之间的主要区别。上面的两个箭头
mu
和nu
不再是二元运算和集合中的单位。相反,您有一个固定的 endofunctorc
。尽管书中有令人困惑的评论,但函子组合*
和恒等函子本身并不能提供 monad 所需的完整结构。另一种方法是与集合
A
的所有自映射的标准幺半群C
进行比较,其中二元运算是组合,可以看出映射标准笛卡尔积C x C
转换为C
。传递到分类幺半群,我们将笛卡尔积x
替换为函子组合*
,并将二元运算替换为自然变换mu
> 来自join
运算符的集合c * c
到c
,这是每个对象T
的 (在编程中键入)。经典幺半群中的恒等元素可以用固定单点集中的地图图像来识别,它被return
运算符的集合所取代。但是现在不再有笛卡尔积了,所以没有元素对,因此没有二元运算。
The answers here do an excellent job in defining both monoids and monads, however, they still don't seem to answer the question:
The crux of the matter that is missing here, is the different notion of "monoid", the so-called categorification more precisely -- the one of monoid in a monoidal category. Sadly Mac Lane's book itself makes it very confusing:
Main confusion
Why is this confusing? Because it does not define what is "monoid in the category of endofunctors" of
X
. Instead, this sentence suggests taking a monoid inside the set of all endofunctors together with the functor composition as binary operation and the identity functor as a monoidal unit. Which works perfectly fine and turns into a monoid any subset of endofunctors that contains the identity functor and is closed under functor composition.Yet this is not the correct interpretation, which the book fails to make clear at that stage. A Monad
f
is a fixed endofunctor, not a subset of endofunctors closed under composition. A common construction is to usef
to generate a monoid by taking the set of allk
-fold compositionsf^k = f(f(...))
off
with itself, includingk=0
that corresponds to the identityf^0 = id
. And now the setS
of all these powers for allk>=0
is indeed a monoid "with product × replaced by composition of endofunctors and unit set by the identity endofunctor".And yet:
S
can be defined for any functorf
or even literally for any self-map ofX
. It is the monoid generated byf
.S
given by the functor composition and the identity functor has nothing do withf
being or not being a monad.And to make things more confusing, the definition of "monoid in monoidal category" comes later in the book as you can see from the table of contents. And yet understanding this notion is absolutely critical to understanding the connection with monads.
(Strict) monoidal categories
Going to Chapter VII on Monoids (which comes later than Chapter VI on Monads), we find the definition of the so-called strict monoidal category as triple
(B, *, e)
, whereB
is a category,*: B x B-> B
a bifunctor (functor with respect to each component with other component fixed) ande
is a unit object inB
, satisfying the associativity and unit laws:for any objects
a,b,c
ofB
, and the same identities for any morphismsa,b,c
withe
replaced byid_e
, the identity morphism ofe
. It is now instructive to observe that in our case of interest, whereB
is the category of endofunctors ofX
with natural transformations as morphisms,*
the functor composition ande
the identity functor, all these laws are satisfied, as can be directly verified.What comes after in the book is the definition of the "relaxed" monoidal category, where the laws only hold modulo some fixed natural transformations satisfying so-called coherence relations, which is however not important for our cases of the endofunctor categories.
Monoids in monoidal categories
Finally, in section 3 "Monoids" of Chapter VII, the actual definition is given:
making 3 diagrams commutative. Recall that in our case, these are morphisms in the category of endofunctors, which are natural transformations corresponding to precisely
join
andreturn
for a monad. The connection becomes even clearer when we make the composition*
more explicit, replacingc * c
byc^2
, wherec
is our monad.Finally, notice that the 3 commutative diagrams (in the definition of a monoid in monoidal category) are written for general (non-strict) monoidal categories, while in our case all natural transformations arising as part of the monoidal category are actually identities. That will make the diagrams exactly the same as the ones in the definition of a monad, making the correspondence complete.
Conclusion
In summary, any monad is by definition an endofunctor, hence an object in the category of endofunctors, where the monadic
join
andreturn
operators satisfy the definition of a monoid in that particular (strict) monoidal category. Vice versa, any monoid in the monoidal category of endofunctors is by definition a triple(c, mu, nu)
consisting of an object and two arrows, e.g. natural transformations in our case, satisfying the same laws as a monad.Finally, note the key difference between the (classical) monoids and the more general monoids in monoidal categories. The two arrows
mu
andnu
above are not anymore a binary operation and a unit in a set. Instead, you have one fixed endofunctorc
. The functor composition*
and the identity functor alone do not provide the complete structure needed for the monad, despite that confusing remark in the book.Another approach would be to compare with the standard monoid
C
of all self-maps of a setA
, where the binary operation is the composition, that can be seen to map the standard cartesian productC x C
intoC
. Passing to the categorified monoid, we are replacing the cartesian productx
with the functor composition*
, and the binary operation gets replaced with the natural transformationmu
fromc * c
toc
, that is a collection of thejoin
operatorsfor every object
T
(type in programming). And the identity elements in classical monoids, which can be identified with images of maps from a fixed one-point-set, get replaced with the collection of thereturn
operatorsBut now there are no more cartesian products, so no pairs of elements and thus no binary operations.
我写这篇文章是为了更好地理解 Mac Lane 的工作数学家的范畴论中臭名昭著的引文的推论。
在描述某事物是什么时,描述它不是什么通常同样有用。
Mac Lane 使用该描述来描述 Monad 的事实可能意味着它描述了 monad 所独有的东西。耐心听我说。为了对这个陈述有更广泛的理解,我认为需要明确的是,他并不是在描述单子所独有的东西;而是在描述单子所特有的东西。该声明同样描述了 Applicative 和 Arrows 等。出于同样的原因,我们可以在 Int 上有两个幺半群(Sum 和 Product),我们可以在 X 上的内函子类别中拥有多个幺半群。但两者还有更多相似之处。
Monad 和 Applicative 都满足标准:
(例如,在日常
Tree a -> List b
中,但在CategoryTree -> List
中)< /p>Tree ->列表
,仅列表->列表
。该语句使用“类别...”这定义了该语句的范围。例如,函子类别描述了 f * -> 的范围。 g *,即
任何函子 ->任何函子
,例如,Tree * ->列表 *
或树 * ->树*
。分类语句未指定的内容描述了允许任何事物。
在这种情况下,在函子内部,
* -> *
又名a -> b
未指定,这意味着Anything ->任何东西,包括任何其他东西
。当我的想象力跳到 Int -> String,还包括Integer ->也许是 Int
,甚至是也许是 Double -> String Int
wherea :: Maybe Double; b :: 要么是 String Int
。因此该语句组合如下:
:: fa -> g b
(即任何参数化类型到任何参数化类型):: fa -> f b
(即,任何一个参数化类型到相同的参数化类型)...换句话说,那么,这个构造的威力在哪里?为了理解完整的动态,我需要看到幺半群的典型绘图(带有单位箭头的单个对象,
:: single object -> single object
)未能说明这一点我可以使用由 Monoid 中允许的 one 类型对象中任意数量的 monoid 值参数化的箭头。等价的 endo, ~ 恒等箭头定义忽略函子的类型值以及最内部“有效负载”层的类型和值。因此,在函子类型匹配的任何情况下,等价都会返回true
(例如,Nothing -> Just * -> Nothing
相当于Just * -> ; 只是 * -> 只是 *
因为它们都是也许 -> 也许
)。侧边栏:~outside 是概念性的,但它是
f a
中最左边的符号。它还描述了“Haskell”首先读入的内容(大图);因此,类型相对于类型值而言是“外部”的。编程中各层(引用链)之间的关系不容易在类别中关联起来。 Set 的类别用于描述类型(Int、Strings、Maybe Int 等),其中包括 Functor 的类别(参数化类型)。引用链:函子类型、函子值(函子集合的元素,例如 Nothing、Just),以及每个函子值指向的其他所有内容。在类别中,关系的描述不同,例如,return :: a -> m a
被认为是从一个 Functor 到另一个 Functor 的自然转换,与迄今为止提到的任何内容都不同。回到主线程,总而言之,对于任何定义的张量积和中性值,该语句最终描述了一个源自其悖论结构的极其强大的计算构造:
:: 列表
);静态fold
未提及有效负载)在 Haskell 中,澄清该语句的适用性非常重要。这种结构的力量和多功能性与单子本身完全无关。换句话说,该构造不依赖于单子的独特性。
当试图弄清楚是否使用共享上下文构建代码来支持相互依赖的计算,而不是可以并行运行的计算时,这个臭名昭著的陈述,尽管它描述了很多,并不是以下选择之间的对比应用性、箭头和单子,而是对它们有多少相同的描述。对于手头的决定,该声明没有实际意义。
这常常被误解。该语句继续描述
join :: m (ma) -> m a
作为幺半群内函子的张量积。然而,它没有阐明在该声明的上下文中如何选择(<*>)
。这确实是“六合一,六合一”的例子。组合值的逻辑完全相同;相同的输入会生成相同的输出(与 Int 的 Sum 和 Product monoids 不同,因为它们在组合 Int 时生成不同的结果)。因此,回顾一下:endofunctors 类别中的幺半群描述:
(<*>)
和(>>=)
都提供对两者的同时访问m
值以计算单个返回值。用于计算返回值的逻辑完全相同。如果不是因为它们参数化的函数形状不同(f :: a -> b
与k :: a -> m b
)以及具有相同计算返回类型的参数(即分别为a -> b -> b
与b -> a -> b
) ,我怀疑我们可以参数化幺半群逻辑,即张量积,以便在两个定义中重用。作为阐明这一点的练习,尝试并实现~t
,最终得到(<*>)
和(>>= )
取决于您决定如何为所有 a b 定义它。如果我的最后一点至少在概念上是正确的,那么它就解释了 Applicative 和 Monad 之间精确且唯一的计算差异:它们参数化的函数。换句话说,差异是这些类型类的实现的外部。
总之,根据我自己的经验,Mac Lane 臭名昭著的引言提供了一个很棒的“goto”模因,它是我在浏览类别时可以参考的指南,以更好地理解 Haskell 中使用的惯用语。它成功地捕获了 Haskell 中可以轻松访问的强大计算能力的范围。
然而,具有讽刺意味的是,我第一次误解了该声明在 monad 之外的适用性,以及我希望在这里传达的内容。它所描述的一切都被证明是 Applicative 和 Monad(以及 Arrows 等)之间的相似之处。它没有说的恰恰是它们之间微小但有用的区别。
I came to this post by way of better understanding the inference of the infamous quote from Mac Lane's Category Theory For the Working Mathematician.
In describing what something is, it's often equally useful to describe what it's not.
The fact that Mac Lane uses the description to describe a Monad, one might imply that it describes something unique to monads. Bear with me. To develop a broader understanding of the statement, I believe it needs to be made clear that he is not describing something that is unique to monads; the statement equally describes Applicative and Arrows among others. For the same reason we can have two monoids on Int (Sum and Product), we can have several monoids on X in the category of endofunctors. But there is even more to the similarities.
Both Monad and Applicative meet the criteria:
(e.g., in day to day
Tree a -> List b
, but in CategoryTree -> List
)Tree -> List
, onlyList -> List
.The statement uses "Category of..." This defines the scope of the statement. As an example, the Functor Category describes the scope of
f * -> g *
, i.e.,Any functor -> Any functor
, e.g.,Tree * -> List *
orTree * -> Tree *
.What a Categorical statement does not specify describes where anything and everything is permitted.
In this case, inside the functors,
* -> *
akaa -> b
is not specified which meansAnything -> Anything including Anything else
. As my imagination jumps to Int -> String, it also includesInteger -> Maybe Int
, or evenMaybe Double -> Either String Int
wherea :: Maybe Double; b :: Either String Int
.So the statement comes together as follows:
:: f a -> g b
(i.e., any parameterized type to any parameterized type):: f a -> f b
(i.e., any one parameterized type to the same parameterized type) ... said differently,So, where is the power of this construct? To appreciate the full dynamics, I needed to see that the typical drawings of a monoid (single object with what looks like an identity arrow,
:: single object -> single object
), fails to illustrate that I'm permitted to use an arrow parameterized with any number of monoid values, from the one type object permitted in Monoid. The endo, ~ identity arrow definition of equivalence ignores the functor's type value and both the type and value of the most inner, "payload" layer. Thus, equivalence returnstrue
in any situation where the functorial types match (e.g.,Nothing -> Just * -> Nothing
is equivalent toJust * -> Just * -> Just *
because they are bothMaybe -> Maybe -> Maybe
).Sidebar: ~ outside is conceptual, but is the left most symbol in
f a
. It also describes what "Haskell" reads-in first (big picture); so Type is "outside" in relation to a Type Value. The relationship between layers (a chain of references) in programming is not easy to relate in Category. The Category of Set is used to describe Types (Int, Strings, Maybe Int etc.) which includes the Category of Functor (parameterized Types). The reference chain: Functor Type, Functor values (elements of that Functor's set, e.g., Nothing, Just), and in turn, everything else each functor value points to. In Category the relationship is described differently, e.g.,return :: a -> m a
is considered a natural transformation from one Functor to another Functor, different from anything mentioned thus far.Back to the main thread, all in all, for any defined tensor product and a neutral value, the statement ends up describing an amazingly powerful computational construct born from its paradoxical structure:
:: List
); staticfold
that says nothing about the payload)In Haskell, clarifying the applicability of the statement is important. The power and versatility of this construct, has absolutely nothing to do with a monad per se. In other words, the construct does not rely on what makes a monad unique.
When trying to figure out whether to build code with a shared context to support computations that depend on each other, versus computations that can be run in parallel, this infamous statement, with as much as it describes, is not a contrast between the choice of Applicative, Arrows and Monads, but rather is a description of how much they are the same. For the decision at hand, the statement is moot.
This is often misunderstood. The statement goes on to describe
join :: m (m a) -> m a
as the tensor product for the monoidal endofunctor. However, it does not articulate how, in the context of this statement,(<*>)
could also have also been chosen. It truly is an example of 'six in one, half a dozen in the other'. The logic for combining values are exactly alike; same input generates the same output from each (unlike the Sum and Product monoids for Int because they generate different results when combining Ints).So, to recap: A monoid in the category of endofunctors describes:
(<*>)
and(>>=)
both provide simultaneous access to the twom
values in order to compute the the single return value. The logic used to compute the return value is exactly the same. If it were not for the different shapes of the functions they parameterize (f :: a -> b
versusk :: a -> m b
) and the position of the parameter with the same return type of the computation (i.e.,a -> b -> b
versusb -> a -> b
for each respectively), I suspect we could have parameterized the monoidal logic, the tensor product, for reuse in both definitions. As an exercise to make the point, try and implement~t
, and you end up with(<*>)
and(>>=)
depending on how you decide to define itforall a b
.If my last point is at minimum conceptually true, it then explains the precise, and only computational difference between Applicative and Monad: the functions they parameterize. In other words, the difference is external to the implementation of these type classes.
In conclusion, in my own experience, Mac Lane's infamous quote provided a great "goto" meme, a guidepost for me to reference while navigating my way through Category to better understand the idioms used in Haskell. It succeeds at capturing the scope of a powerful computing capacity made wonderfully accessible in Haskell.
However, there is irony in how I first misunderstood the statement's applicability outside of the monad, and what I hope conveyed here. Everything that it describes turns out to be what is similar between Applicative and Monads (and Arrows among others). What it doesn't say is precisely the small but useful distinction between them.
注意:不,这不是真的。在某个时候,丹·皮波尼本人对这个答案发表了评论,说这里的因果关系完全相反,他写这篇文章是为了回应詹姆斯·艾里的俏皮话。但它似乎已经被删除了,也许是被一些强迫性的整理者删除了。
以下是我原来的回答。
Iry 很可能读过 从 Monoid 到 Monads,一篇文章,其中 Dan Piponi (sigfpe) 从 Haskell 中的幺半群派生出 monad,其中对范畴论进行了大量讨论,并明确提到了“Hask" 。无论如何,任何想知道单子作为内函子类别中的幺半群意味着什么的人可能会从阅读这个推导中受益。
Note: No, this isn't true. At some point there was a comment on this answer from Dan Piponi himself saying that the cause and effect here was exactly the opposite, that he wrote his article in response to James Iry's quip. But it seems to have been removed, perhaps by some compulsive tidier.
Below is my original answer.
It's quite possible that Iry had read From Monoids to Monads, a post in which Dan Piponi (sigfpe) derives monads from monoids in Haskell, with much discussion of category theory and explicit mention of "the category of endofunctors on Hask" . In any case, anyone who wonders what it means for a monad to be a monoid in the category of endofunctors might benefit from reading this derivation.
这是另一个使用 monad IO 的显式示例。
可能的混淆1:A
Monad
不是Monoid
!在 Haskell 文档中
在上面的 Monoid
中,你可以看到源代码是这样写的:“Define
Monoid
to be a typeclass: a typea
is a幺半群
如果它是半群
和⟨附加条件⟩”。您在
Monad
。因为Monad
不是 Haskell 幺半群。为什么? “monoid”这个词有 2 个含义:
S
配备●:S × S → S
和e ∈ S
,Haskell 中的类型类 Monoid 是抽象代数意义上的幺半群。
特别是,
IO
当然不包含像ℕ
那样的内容。)+
接受两个整数并返回一个,但是join
不接受两个任何东西 ---IO
内部没有任何东西可以拿!)可能的混淆2:monad(category_theory)不是Haskell
Monad
不同概念之间的关系:
抽象代数中的幺半群是抽象代数中的幺半群(范畴论) >集合类别。
这是一个等价——集合范畴中的幺半群是抽象代数中的幺半群。
单子是内函子类别中的幺半群(范畴论)。
这也是等价的。
Haskell 中的
Monad
是一个 monad。例如,IO
是一个 monad。这不是等价!并非所有 monad 都是 Haskell monad。
范畴论中的幺半群是什么?
需要澄清的是,这里滥用了术语。当我们说
ℤ
是我们实际上指的当然,使用哪些运算符通常是隐式的。
同样,当我们说
我们所说的
另一方面,
(ℕ, ×, 1)
也是一个幺半群。当我们说
这里同样存在符号滥用。它实际上意味着
,或者
或, 更短,
(我将在这里使用 η (eta)。其他一些答案使用 ν (nu)。)
示例
考虑上面的
ℕ
示例。我们想说您可以检查事物的类型:
ℕ
是集合类别中的对象。+
是从ℕ × ℕ
到ℕ
的集合的态射。⟨• → 0⟩
是从单例集{•}
到ℕ
的集合态射,发送唯一的元素•
到0
。×
是一个Sets2 → Sets
函子,ℕ × ℕ
是Sets,
{•}
是类别Sets
中的单例集。那么什么是 Haskell monad?
Hask
类别是其中String
、Integer
等)的类别,(+ 1) :: Integer -> Integer
,read :: String -> Integer
)。对于任何两个类别
A
和B
,我们都有函子类别,这里表示为Fun(A, B)
,其中A
和B
之间,(如果您不知道函子和自然变换是什么,请阅读它们。)
具体来说,如果
A = B
,则类别Fun(A, A)
code> 是A
的endofunctors 类别。将这些拼凑在一起,
这里,
Compose
是一个Fun(Hask, Hask)2 → Fun(Hask, Hask)
函子,Compose IO IO
是将String
发送到IO (IO String)
的 endofunctor,Identity
是Fun(Hask, Hask) 类别中的对象
,身份字符串
等于字符串
。例子
这意味着:
IO
是Fun(Hask, Hask)
类别中的对象代码>:Hask
类别中的每个对象,IO
将其发送到另一个对象。例如,从
String
,您将获得IO String
。 (请注意,String
是一种类型,但在Hask
类别中,它是一个对象。)对于
Hask
类别中的每个态射,IO
将其发送到另一个态射。例如从
read :: String ->整数
,它被映射到fmap read :: IO String -> IO 整数
.return
是类别Fun(Hask, Hask)
中的态射。也就是说,它是从函子
Identity
到函子IO
的自然转换。这是一些图表。
join
是类别Fun(Hask, Hask)
中的态射。也就是说,它是从函子
Compose IO IO
到函子IO
的自然转换。(函子
Compose IO IO
是什么?它只接受String
并返回IO (IO String)
)。((IO, fmap), join, return)
满足幺半群公理。这不是定义吗?
事实上,有两种等效的方法来定义 monad。
(IO, bind, return)
--- 其中bind
写为>>=
。(IO, fmap, join, return)
。它们是可以互换的:
bind
可以通过fmap
和join
实现,如下所示:join
和fmap 可以通过
bind
实现,如下所示:或者在 Haskell 语法糖中(
do
符号去糖到return
和bind
):可以缩短为
参考文献
Here is another take with an explicit example using the monad
IO
.Possible confusion 1: A
Monad
is not aMonoid
!In the Haskell documentation for
Monoid
above, you can see the source code readsIn English: "Define
Monoid
to be a typeclass: a typea
is aMonoid
if it is aSemigroup
and ⟨additional conditions⟩".You don't quite see the same thing in
Monad
. Because aMonad
is not a Haskell monoid.Why? The word "monoid" has 2 meanings:
S
equipped with●: S × S → S
ande ∈ S
,The typeclass
Monoid
in Haskell is a monoid in the abstract algebra sense.In particular,
IO
certainly don't contain anything likeℕ
does.)+
takes in two integers and return one, butjoin
does not take in two anything --- there isn't anything insideIO
to take!)Possible confusion 2: a monad (category_theory) is not a Haskell
Monad
The relationship between the different concepts:
A monoid in abstract algebra is a monoid (category theory) in the category of sets.
This is an equivalence --- a monoid in the category of sets is a monoid in abstract algebra.
A monad is a monoid (category theory) in the category of endofunctors.
This is also an equivalence.
A
Monad
in Haskell is a monad. For example,IO
is a monad.This is not an equivalence! Not all monads are Haskell monads.
What is a monoid in category theory?
For clarification, there's an abuse of terminology here. When we say
we actually means
Of course, which operators are being used are often implicit.
Similarly, when we say
we means
On the other hand,
(ℕ, ×, 1)
is also a monoid.When we say
there is similarly an abuse of notation here. It actually means
or
or, shorter,
(I'll use η (eta) here. Some other answers use ν (nu).)
Example
Consider the example of
ℕ
above. We wants to sayYou can check the types of things:
ℕ
is an object in the category of sets.+
is a morphism of sets fromℕ × ℕ
toℕ
.⟨• → 0⟩
is a morphism of sets from the singleton set{•}
toℕ
, that sends the only element•
to0
.×
is aSets2 → Sets
functor,ℕ × ℕ
is an object inSets
,{•}
is a singleton set in the categorySets
.So what is a Haskell monad?
The
Hask
category is the category whereString
,Integer
, etc.),(+ 1) :: Integer -> Integer
,read :: String -> Integer
).For any two categories
A
andB
, we have the category of functors, here denotedFun(A, B)
whereA
andB
,(If you don't know what a functor and a natural transformations are, read up on them.)
Specifically, if
A = B
, then the categoryFun(A, A)
is the category of endofunctors ofA
.Piecing these together,
Here,
Compose
is aFun(Hask, Hask)2 → Fun(Hask, Hask)
functor,Compose IO IO
is the endofunctor that sendsString
toIO (IO String)
,Identity
is an object in the categoryFun(Hask, Hask)
,Identity String
is equal toString
.Example
which means:
IO
is an object in the categoryFun(Hask, Hask)
:from each object in the category
Hask
,IO
sends it to another object.e.g. from
String
, you getIO String
. (Note thatString
is a type, but in the categoryHask
it is an object.)from each morphism in the category
Hask
,IO
sends it to another morphism.e.g. from
read :: String -> Integer
, it gets mapped tofmap read :: IO String -> IO Integer
.return
is a morphism in the categoryFun(Hask, Hask)
.That is, it is a natural transformation from the functor
Identity
to the functorIO
.Here are some diagrams.
join
is a morphism in the categoryFun(Hask, Hask)
.That is, it is a natural transformation from the functor
Compose IO IO
to the functorIO
.(What is the functor
Compose IO IO
? It just takes in e.g.String
and returnsIO (IO String)
).((IO, fmap), join, return)
satisfies the monoidal axioms.That's not the definition?
Indeed, there are two equivalent ways to define a monad.
(IO, bind, return)
--- wherebind
is written as>>=
.(IO, fmap, join, return)
.They're interchangeable:
bind
can be implemented withfmap
andjoin
as follows:join
andfmap
can be implemented withbind
as follows:Or in Haskell syntactic sugar (the
do
notation desugar toreturn
andbind
):which can be shortened to
References