gpt4 book ai didi

scala - 带有 flatMap 的集合是单子(monad)吗?

转载 作者:行者123 更新时间:2023-12-04 00:04:27 24 4
gpt4 key购买 nike

Scala 具有 Iterable[A] 的特性定义

def flatMap[B](f: (A) ⇒ GenTraversableOnce[B]): Iterable[B] 

这看起来确实像 monad 上的 bind 函数,并且文档暗示它是一个 monad,但是有两个反对意见,一个是次要的,一个是主要的:
  • 次要的:传入的函数的返回类型是 GenTraversableOnce .我认为这只是一种在判断 monad-ness 时可以忽略的便利。
  • 主要的:monad 的“值”是它包含的所有值的列表,但函数一次只给一个值。

  • 这些问题会破坏集合的单子(monad)性吗?

    最佳答案

    您的标题问题的答案是 也许 .与 flatMap 的集合单子(monad)是不够的,但如果它满足一些进一步的条件,它可能是单子(monad)。
    您的“次要”问题肯定会破坏 Iterable 的单性(“monad-ness”的正确词)。 .这是因为 Iterable 的许多子类型和 GenTraversableOnce不是单子(monad)。因此,Iterable不是单子(monad)。
    您的“主要”问题根本不是问题。例如,List 的函数参数monad 的 flatMap接收 List 的元素一次一个。列表的每个元素都会生成一个完整的结果列表,并且这些列表在最后都连接在一起。
    幸运的是,判断某个东西是否是 monad 真的很容易!我们只需要知道 monad 的精确定义。
    成为单子(monad)的要求

  • monad 必须是类型构造函数 F[_]这需要一个类型参数。例如,F可能是 List , Function0 , Option
  • 一元单位。这是一个接受任何类型值的函数 A并产生一个 F[A] 类型的值.
  • 一元组合操作。这是一个采用 A => F[B] 类型函数的操作。 , 和 B => F[C] 类型的函数并产生 A => F[C] 类型的复合函数.

  • (还有其他方式来说明这一点,但我发现这个表述很容易解释)
    Iterable 考虑这些.它肯定需要一种类型的参数。它在函数 Iterable(_) 中有一个排序单位。 .而它的 flatMap操作并不严格符合,我们当然可以写:
    def unit[A](a: A): Iterable[A] = Iterable(a)

    def compose[A,B,C](f: A => Iterable[B],
    g: B => Iterable[C]): A => Iterable[C] =
    a => f(a).flatMap(g)
    但这并不能使它成为一个 monad,因为一个 monad 还必须满足某些定律:
  • 关联性:compose(compose(f, g), h) = compose(f, compose(g, h))
  • 身份:compose(unit, f) = f = compose(f, unit)

  • 打破这些法律的简单方法, as lmm has already pointed out , 是混合 SetList作为 Iterable在这些表达中。
    “半单子(monad)”
    而只有 flatMap 的类型结构(而不是 unit ),不是单子(monad),它可能形成所谓的 Kleisli semigroupoid。要求与 monad 相同,只是没有 unit操作和没有身份法。
    (关于术语的注释:一个 monad 构成了一个 Kleisli 范畴,一个 semigroupoid 是一个没有身份的范畴。)
    为了理解
    Scala 的 for-comprehensions 在技术上比 semigroupoids 的要求更少(只是 mapflatMap 操作不遵守任何法律)。但是将它们与至少不是半群的事物一起使用会产生非常奇怪和令人惊讶的效果。例如,这意味着您不能在 for-comprehension 中内联定义。如果你有
    val p = for {
    x <- foo
    y <- bar
    } yield x + y
    以及 foo的定义是
    val foo = for {
    a <- baz
    b <- qux
    } yield a * b
    除非结合律成立,否则我们不能依赖能够将其重写为:
    val p = for {
    a <- baz
    b <- qux
    y <- bar
    } yield a * b + y
    无法进行这种替换是非常违反直觉的。所以大多数时候,当我们使用 for-comprehensions 时,我们假设我们在一个 monad 中工作(可能即使我们没有意识到这一点),或者至少是一个 Kleisli semigroupoid。
    但请注意,这种替换通常不适用于 Iterable。 :
    scala> val bar: Iterable[Int] = List(1,2,3)
    bar: Iterable[Int] = List(1, 2, 3)

    scala> val baz: Iterable[Int] = Set(1,2,3)
    baz: Iterable[Int] = Set(1, 2, 3)

    scala> val qux: Iterable[Int] = List(1,1)
    qux: Iterable[Int] = List(1, 1)

    scala> val foo = for {
    | x <- bar
    | y <- baz
    | } yield x * y
    foo: Iterable[Int] = List(1, 2, 3, 2, 4, 6, 3, 6, 9)

    scala> for {
    | x <- foo
    | y <- qux
    | } yield x + y
    res0: Iterable[Int] = List(2, 2, 3, 3, 4, 4, 3, 3, 5, 5, 7, 7, 4, 4, 7, 7, 10, 10)

    scala> for {
    | x <- bar
    | y <- baz
    | z <- qux
    | } yield x * y + z
    res1: Iterable[Int] = List(2, 3, 4, 3, 5, 7, 4, 7, 10)
    有关单子(monad)的更多信息
    有关 Scala 中 monad 的更多信息,包括它的全部含义以及我们应该关心的原因,我鼓励您查看 chapter 11 of my book.

    关于scala - 带有 flatMap 的集合是单子(monad)吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27750046/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com