人们通常说类型是一种单子。
在一些函数式语言和库(如Scala / Scalaz)中,您有一个类型构造器,例如List或Option,您可以定义一个单子实现,该实现与原始类型分开。因此,在类型系统中基本上没有任何禁止您为相同的类型构造器创建不同的单子实例的限制。
- 类型构造器是否可以拥有多个单子?
- 如果是,您能否提供任何有意义的示例?任何“人造”的例子?
- 关于单子、半群、应用等呢?
人们通常说类型是一种单子。
在一些函数式语言和库(如Scala / Scalaz)中,您有一个类型构造器,例如List或Option,您可以定义一个单子实现,该实现与原始类型分开。因此,在类型系统中基本上没有任何禁止您为相同的类型构造器创建不同的单子实例的限制。
在数学中,我们经常可以找到以下内容:
单子是一个三元组(T, return, bind)
,满足(...)。当bind
和return
可以从上下文推断出来时,我们只需要将单子称为T
。
幺半群是一个三元组(M, e, •)
,满足(...)。(...)我们只需将幺半群称为M
。
拓扑空间是一对(S, T)
,满足(...)。我们只需将拓扑空间称为S
。
环是一个五元组(V, 0, +, 1, ×)
...
因此,对于给定的类型构造器T
,可能会有多个不同的return
和bind
定义使得一个单子。为避免每次都引用三元组,我们可以给T
不同的名称以消除歧义,这种方式对应于Haskell中的newtype
构造。例如:[]
vs ZipList
,State s
vs ReaderT s (Writer s)
。
附言:将单子或幺半群定义为三元组有些人为,特别是考虑到存在不同的表示方法:我们也可以说单子是一个三元组(T, fmap, join)
,或者幺半群是一对(M, •)
,其中恒等元素隐藏在额外的条件中(因为它由•
唯一确定)。数学结构的本体论是一个更为哲学的问题,超出了SO的范围(以及我的专业知识范围)。但更谨慎地重新表述这样的定义可能是说“单子通过三元组(T, return, bind)
来定义|表征”。
State s
和ReaderT s (Writer s)
的例子,它们都由函数s -> (s, a)
组成。([]
和ZipList
是可应用函子的例子,但ZipList
不是单子)。Succs
类型具有单子实例,用于定义收缩,但也有一个乘积单子(Product Identity []
)。 - Li-yao XiaMonad
实例的例子,那是不可能的,因为实例必须是唯一的。 - Li-yao Xia(T, return1, bind1)
和 (T, return2, bind2)
。 - Sebastien LorberZipList
就是一个例子(只不过它是 Applicative,而不是 Monad,但原理相同)。 - Will NessMonad
实例。”也就是说,单子经常被用作单子化的同义词,修饰其他名词以产生一个短语(单子问题,Monad
实例),有时被用作动词have的宾语。Monad
的实例,一个Monoid
的实例等等。当一个给定的类型可以定义许多这样的实例时,例如数字在加法、乘法、最大值、最小值和许多其他操作下都是单子群,Haskell定义了分离的类型,例如Sum Int
,一个在Int
上的Monoid
实例,其中操作是+
,以及Product Int
,一个Monoid
实例,在其中操作是*
。
List
和Option
不是类型,因为我们不能真正拥有例如def f(x: Option) = ...
(我认为即使在Scala中也会禁止这样做,但由于JVM的擦除,我不确定是否100%正确)。仅像List[Int],Option[Bool]
这样的才是类型。在Haskell中,我们称List、Option
之类的东西为类型构造函数,其种类为* -> *
。在Scala中我不知道。事实上,对于像Int
这样的类型,我们不会说它是单子,也不会说它拥有一个单子。对于List[Int]
,我们可以非正式地说它是一个单子类型。 - chi