List 是如何成为 monad 的?

How is List a monad?

我认为我对 Monad 和 monadic 操作有基本的了解,但在理解如何将 monadic 类型的神奇功能添加到基础类型上仍然有点困惑(希望这是有道理的)。

例如,我 was reading 关于 List[T] 如何成为 monad。但是,如果我 flatMapmapfor 理解中顺序地处理一些列表,那么 flatMapmap 是否真的提供了 monadic 魔法?

如果我创建一个 List<String> 那么如何添加 monadic 魔法?还是 List<T> 在 Scala 中总是一个 monad,因为它恰好是该语言已经为其提供内置 monadic 支持的那些容器之一?

Monad 是一个 概念 而不是 class 或特征。 因此 List[T] 满足 monad https://en.wikipedia.org/wiki/Monad_(functional_programming) 的所有要求,它可以被称为 monadic。 说白了就是monad,因为它具备monad提供的所有功能和能力,所以它是monad。

如果你想要更多的保证并更明确地表达成为一个单子你可以使用Scalaz。 link 展示了如何并详细介绍了 monad 法则:http://eed3si9n.com/learning-scalaz/Monad+laws.html

你完全正确 flatMapmap 提供 "monadic magic." 幸运的是或不幸的是(取决于你看到了多少糟糕的代码)在编程。再多的抽象也无法使您(或其他人)免于最终编写出您想要的代码。抽象 "just" 让您可以重复使用以前编写的代码并阐明您对问题的想法。那么 monad 只是一个概念、一个想法、一个抽象等等。

在 Scala 的情况下,这实际上就是编译器对 for 推导所做的,它变成了一系列 flatMapmapwithFilter,和 filter 语句。

一个 monad(在 Scala 中)可以被认为只是一个标签,你碰巧有一个类型构造函数 T[_] 和两个函数 1

def f0[A](x: T[A], f: X => T[A]): T[A]
def f1[A](x: A): T[A]

按照惯例,当他们看到这种现象时,Scala 社区会调用 f0 flatMap 并且通常使它成为一种方法,以便 x 始终是父 class 而不是单独的参数。还有一个约定叫 f1 pointpure(参见 scalazcats)。 f1 通常也是一种方法,这样它就不会显式地接受参数而只是使用其父 class 作为 x.

每当有人说 "such-and-such" 是一个单子时,总会有一个隐含的 f0f1 说话者希望听众推断出来。严格来说,“List 是一个单子”是对术语的轻度滥用。它是 List 的简写,连同函数 (xs: List[A], f: A => List[A]) => xs.map(f).flatten(形成 f0)和 (x: A) => List(x)(形成 f1)形成一个 monad。或者稍微不那么迟钝,List 连同列表上的标准 flatMapList.apply 构造函数形成一个 monad。

因此从来没有任何魔法。作为 class 将某物化为 Monad 的一部分,您必须提供 flatMappure.

的概念

有很多方法可以将这种抽象的 monad 转化为代码。天真的方法(即没有第三方库的 Scala)只是就 f0f1(例如 flatMap)的通用名称达成一致,并仅命名具有适当名称的方法类型签名那些名字。这基本上就是 scalac 期望你为 for 理解做的事情。您可以更进一步,尝试使用 traitabstract class 将事物形式化。也许称它为 Monad 是为了可爱并拥有类似以下的东西:

trait Monad[A] {
  def flatMap(f: A => Monad[A]): Monad[A]

  def pure(x: A): Monad[A]
}

然后你可以将任何扩展这个 Monad 的东西称为 monad 思想的实现(你可以想象像 class List[A] extends Monad[A] 这样的东西)。

由于各种实际原因,结果证明这不太令人满意,因此您最终得到了通常的解决方案,看起来像(挥手消除了许多其他复杂性)

trait Monad[F[_]] {
  def flatMap[A](f: A => F[A]): F[A]

  def pure[A](x: A): F[A]
}

implicits 实施。


脚注:

  1. 还有一些 laws/conventions 管理他们的互动。这些定律存在的实际原因是让程序员的生活保持理智,这样他们就知道当有人告诉他们这些函数是 "monadic." 时会发生什么 "monadic."但我不会在这里深入研究它们,因为它们在其他地方有充分的解释。

从纯粹的角度来看,List可能不是一个monad。至少那是我基于左身份规则的看法:

unit(x).flatMap(f) == f(x)

如果 List 支持左恒等式规则,则下列情况应为真:

List(2).flatMap(e => e*e) == 4

但事实并非如此,如果您尝试上面的代码,编译器会在您编码时呕吐。