cats.data.WriterT 的 for-comprehension 中忽略的参数

Ignored parameter in for-comprehension for cats.data.WriterT

我正在和猫一起经历 scala。在 Writer ($4.7.2, p. 111) 的示例中,使用了以下 for-comprehension:

import cats.data.Writer
import cats.syntax.writer._
import cats.syntax.applicative._
import cats.instances.vector._
type Logged[A] = Writer[Vector[String], A]

val writer1 = for {
  a <- 10.pure[Logged]
  _ <- Vector("a", "b", "c").tell
  b <- 32.writer(Vector("x", "y", "z"))
} yield a + b
// writer1: cats.data.WriterT[cats.Id,Vector[String],Int] = WriterT((
Vector(a, b, c, x, y, z),42))

据我所知,下划线 (_) 用于忽略的参数,它从不在 yield 关键字之后使用。仍然将值 "a""b""c" 写入日志。这是成语还是有其他解释?

Writer monad 可以被认为是一个元组,其中第一个元素表示日志值,而第二个元素表示主要业务值。关键要明白

中的下划线_
for {
  a <- 10.pure[Logged]
  _ <- Vector("a", "b", "c").tell
  b <- 32.writer(Vector("x", "y", "z"))
} yield a + b

代表“业务价值”,即元组的第二个 ._2 元素,而不是整个元组,因此在组合中此时只忽略业务价值。如果我们对 for-comprehension

进行去糖处理,也许会有帮助
WriterT[Id, Vector[String], Int](Vector(), 10).flatMap { (a: Int) =>
  WriterT[Id, Vector[String], Unit](Vector("a", "b", "c"), ()).flatMap { (_: Unit) =>
    WriterT[Id, Vector[String], Int](Vector("x", "y", "z"), 32).map { (b: Int) =>
      a + b
    }
  }
}

这样我们就看不到任何不寻常的事情发生;参数 (_: Unit) 根本没有在 (_: Unit) => body 的正文中使用。现在让我们也看看 flatMap

def flatMap[U](
  f: V => WriterT[F, L, U]
)(implicit flatMapF: FlatMap[F], semigroupL: Semigroup[L]): WriterT[F, L, U] =
  WriterT {
    flatMapF.flatMap(run) { lv =>
      flatMapF.map(f(lv._2).run) { lv2 =>
        (semigroupL.combine(lv._1, lv2._1), lv2._2)
      }
    }
  }

很少有东西会立即弹出

  • 元组表示法 ._1._2
  • semigroupL.combine(lv._1, lv2._1)
  • f(lv._2)

我们看到了如何使用半群来组合日志,日志是元组的第一个元素。在我们的例子中分析 f(lv._2),我们有 lv._2 = ()f 是函数 (_: Unit) => body,其中参数 (_: Unit) 根本没有在 body 中使用。

一般来说,flatMap 的特殊定义赋予了 monad 特有的力量。在 Writer 的情况下,这种能力允许它在我们沿着计算链前进时透明地组合日志。


作为旁注,正如 Luis 所说,这里没有 side 效应发生。在下面的句子

中考虑术语效果的函数式编程意义
 - Identity monad encodes the effect of having no effect
 - IO       monad encodes the effect of having a side-effect
 - Option   monad encodes the effect of having optionality

effect 的语义由 flatMap 的实现编码。