实践中的单子作为幺半群

5

我想更好地理解单子(monads)和幺半群(monoids)之间的关系,希望能从实际应用中了解这个问题。如果我的问题不太明确,还请多多包涵,因为我还在学习中。

例如,假设我有以下内容:

trait Monoid[T] {
  def zero: T
  def combine: (T,T) => T
}

以下内容来源于这里

trait Monad[+M[_]] {
  def unit[A](a: A): M[A]
  def bind[A, B](m: M[A])(f: A => M[B]): M[B]
}

Monad和Monoid特征之间是否存在关系?例如,我是否可以将Monad视为Monoid(假设我正确理解Monad是Monoid的一种特殊情况)?


1
可能是Scala中的单子与幺半群重复问题 - Paweł Jurczenko
我不认为这是重复的。链接的答案更多是理论性的,而我的问题更加注重代码方面。但是链接的被接受的答案说“它们在实践中完全不同”(在实际应用中没有关系),所以如果这是正确的代码答案,我将接受它作为重复的答案。 - Will I Am
1个回答

5

如果你使用unitjoin而不是unitbind来编写一个单子,那么你可能会更容易地看到它们之间的联系:

trait Monoid[T] {
  def zero: T
  def combine: (T,T) => T
}

trait Monad[M[_]] {
  def unit[A]: A => M[A]
  def join[A]: M[M[A]] => M[A]
}

在Scala中,Join相当于flatten,而Bind相当于flatMap

请注意,为了仅使用unitflatten/join定义一个monad,您还必须提供方法map[A](m: M[A])(f: A => B): M[B]。这是因为monad实际上是具有两个自然变换(unit和join)的(endo)functor。由于它是一个functor,它具有map功能。根据您的代码设计,map应该与unitjoin一起在您的Monad trait内定义,或者从某个Functor trait继承,该trait将被您的Monad trait扩展。

为了完整起见,让我声明定义monad的所有三种可能方式:

  • unit + flatMap
  • unit + flatten + map
  • unit + compose

这三种方法都可以使用其他两种方法之一来表达。我将跳过演示代码,因为它与问题无直接关系,但如果需要,我可以在编辑中添加它。


join 的类型为 M[M[A]] => M[A] - Lee
没错,已经解决了。我之前想的是 M x M → M 表示自然变换的符号。 - slouc
1
谢谢,我也很感激你列出了定义单子的三种可能方式,我在其他阅读中没有看到过这么简洁明了的表述。 - Will I Am

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