【问题标题】:How is List a monad?List 是怎样的一个单子?
【发布时间】:2016-02-29 11:34:12
【问题描述】:

我认为我对 Monad 和 monadic 操作有基本的了解,但我仍然有点坚持理解如何将 monadic 类型的神奇特性添加到基础类型中(希望这是有道理的)。

例如,我was reading 关于List[T] 如何是一个单子。但是,如果我 flatMapmapfor 理解中依次遍历一些列表,那么真的是 flatMapmap 提供单子魔法吗?

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

【问题讨论】:

  • List 作为数据结构不是 Monad,但 Scala 的 List 实现 flatMap 的事实赋予了它 monadic 的超能力。它还需要满足associativity、左单元和右单元法则才有资格成为 Monad。
  • 没有魔法。只有法律。
  • 我想你会喜欢这个链接medium.com/@sinisalouc/…
  • @hasumedia,感谢您的评论和链接。
  • 希望对您有所帮助:Why do they say "List is a Monad"

标签: scala monads


【解决方案1】:

flatMapmap 提供“一元魔法”是完全正确的。幸运或不幸(取决于你见过多少糟糕的代码)编程没有魔法。再多的抽象也无法使您(或其他人)免于最终编写执行您想要的事情的代码。抽象“只是”让您可以重用以前编写的代码并澄清您对问题的想法。那么,monad 只是一个概念、一个想法、一个抽象等。

在 Scala 的情况下,这实际上就是编译器对 for 的理解,它变成了一系列 flatMapmapwithFilterfilter 语句。

可以将 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 始终是父类而不是单独的参数。还有一个约定叫f1pointpure(见scalazcats)。 f1 通常也是一种方法,因此它最终不会显式地接受参数,而只是将其父类用作 x

每当有人说“某某”是一个单子时,总有一个隐含的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。

因此从来没有任何魔法。作为将某物分类为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. 以及一些管理它们相互作用的法律/公约。这些定律存在的实际原因是为了让程序员的生活更加清醒,这样当有人告诉他们这些函数是“单子的”时,他们知道会发生什么。正是这些规律使得推理诸如 monad 之类的结构如此有用,但我不会在这里深入研究它们,因为它们在其他地方已经得到充分解释。

【讨论】:

  • 所以我们可以称“Scala 中的列表”是 monad,对吗?因为 Scala 中的 List 实现了 2 个形成 monad 定义的方法。谢谢。
【解决方案2】:

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

如果你想要更多的保证并更明确地表达作为一个单子,你可以例如使用Scalaz。 此链接显示了单子定律的方法并提供了详细信息:http://eed3si9n.com/learning-scalaz/Monad+laws.html

【讨论】:

    【解决方案3】:

    从纯粹的角度来看,List 可能不是单子。至少这是我基于左身份规则的看法:

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

    如果List 支持左身份规则,那么以下应该为真:

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

    但事实并非如此,如果你尝试上面的代码,编译器会在你编码时出错。

    【讨论】:

    • 这写的很简单。您的示例失败,因为e => e*e 没有正确的类型flatMap 的函数参数。也就是说,您正在传递一个Int => Int,而您应该传递一个Int => List(Int)。您可以通过传递e => List(e*e) 来解决此问题。然后我们有List(2).flatMap(e => List(e*e)) == List(4)List' 的恒等律和结合律都成立!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-10-28
    • 1970-01-01
    • 1970-01-01
    • 2018-05-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多