一个类型构造器是单子还是拥有单子?

6

人们通常说类型是一种单子。

在一些函数式语言和库(如Scala / Scalaz)中,您有一个类型构造器,例如List或Option,您可以定义一个单子实现,该实现与原始类型分开。因此,在类型系统中基本上没有任何禁止您为相同的类型构造器创建不同的单子实例的限制。


  • 类型构造器是否可以拥有多个单子?
  • 如果是,您能否提供任何有意义的示例?任何“人造”的例子?
  • 关于单子、半群、应用等呢?

2
Monad实例不是唯一的。类型系统不允许一个类型有多个实例,但你可以使用newtype定义新的实例。 - Cubic
谢谢 @Cubic。我会尝试阅读,但目前对Haskell不太熟悉。这个问题与编程语言无关。 - Sebastien Lorber
3
术语细节:ListOption不是类型,因为我们不能真正拥有例如def f(x: Option) = ...(我认为即使在Scala中也会禁止这样做,但由于JVM的擦除,我不确定是否100%正确)。仅像List[Int],Option[Bool]这样的才是类型。在Haskell中,我们称List、Option之类的东西为类型构造函数,其种类为* -> *。在Scala中我不知道。事实上,对于像Int这样的类型,我们不会说它是单子,也不会说它拥有一个单子。对于List[Int],我们可以非正式地说它是一个单子类型。 - chi
感谢 @chi,我已经编辑了这个问题。 - Sebastien Lorber
2个回答

15

在数学中,我们经常可以找到以下内容:

  • 单子是一个三元组(T, return, bind),满足(...)。当bindreturn可以从上下文推断出来时,我们只需要将单子称为T

  • 幺半群是一个三元组(M, e, •),满足(...)。(...)我们只需将幺半群称为M

  • 拓扑空间是一对(S, T),满足(...)。我们只需将拓扑空间称为S

  • 环是一个五元组(V, 0, +, 1, ×)...

因此,对于给定的类型构造器T,可能会有多个不同的returnbind定义使得一个单子。为避免每次都引用三元组,我们可以给T不同的名称以消除歧义,这种方式对应于Haskell中的newtype构造。例如:[] vs ZipListState s vs ReaderT s (Writer s)


附言:将单子或幺半群定义为三元组有些人为,特别是考虑到存在不同的表示方法:我们也可以说单子是一个三元组(T, fmap, join),或者幺半群是一对(M, •),其中恒等元素隐藏在额外的条件中(因为它由唯一确定)。数学结构的本体论是一个更为哲学的问题,超出了SO的范围(以及我的专业知识范围)。但更谨慎地重新表述这样的定义可能是说“单子通过三元组(T, return, bind)来定义|表征”。


感谢数学方面的解释。您有任何示例,其中有多个单子元素用于同一类型构造函数吗?最好是实用性强、有用的示例,但如果不可能,那么也可以是一些“人工”的例子,这也很有趣。 - Sebastien Lorber
我之前举了State sReaderT s (Writer s)的例子,它们都由函数s -> (s, a)组成。([]ZipList是可应用函子的例子,但ZipList不是单子)。Succs类型具有单子实例,用于定义收缩,但也有一个乘积单子(Product Identity [])。 - Li-yao Xia
如果你字面上的意思是为相同类型构造器提供Monad实例的例子,那是不可能的,因为实例必须是唯一的。 - Li-yao Xia
我不确定是否理解正确。从“对于给定的类型构造器T,可能存在多个不同的return和bind定义,使得它成为一个monad”这句话中,我理解为同一类型构造器可以有两个实例:(T, return1, bind1)(T, return2, bind2) - Sebastien Lorber
3
在数学中是可以的,但在 Haskell 中对于给定类型只能定义一个 Monad 实例。不过,在 Haskell 中也有“newtype”,它允许我们用不同的名称来表示相同的类型;对于这个新名称,我们可以定义另一个 Monad 实例。 ZipList 就是一个例子(只不过它是 Applicative,而不是 Monad,但原理相同)。 - Will Ness

2
就您所问的语言用法而言,谷歌表示,“有一个单子”这个短语似乎并不常用于您所询问的方式。大多数真实出现的情况是在句子中,例如“Haskell社区有一个单子问题”。然而,在野外存在一些模糊类似的用法,例如“唯一使其‘单子化’的是它具有Monad实例。”也就是说,单子经常被用作单子化的同义词,修饰其他名词以产生一个短语(单子问题,Monad实例),有时被用作动词have的宾语。
关于编码:在Haskell中,类型可以声明一个Monad的实例,一个Monoid的实例等等。当一个给定的类型可以定义许多这样的实例时,例如数字在加法、乘法、最大值、最小值和许多其他操作下都是单子群,Haskell定义了分离的类型,例如Sum Int,一个在Int上的Monoid实例,其中操作是+,以及Product Int,一个Monoid实例,在其中操作是*
不过我还没有全面检查过成千上万个结果,所以很可能有更好的例子符合你所问的问题。
我经常看到的措辞是我刚才用的那个:一个类型 在一个操作下的类别

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接