首页 > 解决方案 > cat.data.WriterT 的理解中忽略的参数

问题描述

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

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"写入日志。这是成语还是有其他解释?

标签: scalascala-cats

解决方案


Writermonad 可以被认为是一个元组,其中第一个元素表示对数值,而第二个元素表示主要业务值。_关键是要理解

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

代表“业务价值”,即元组的第二个._2元素,而不是整个元组,所以在组合中此时只忽略业务价值。如果我们对理解不加糖,也许会有所帮助

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)

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

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


作为旁注,正如路易斯所说,这里没有副作用发生。考虑以下句子中术语效果的函数式编程意义

 - 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

效果的语义由flatMap.


推荐阅读