猫flatTap或scalatap

我很难理解猫 flatTap 的用途是什么?

def flatTap[B](f: A => F[B]): F[A] = typeClassInstance.flatTap[A, B](self)(f)

实际上,我只是想利用记录效果的价值,然后返回该效果。

似乎 scala 2.13 tap 是要走的路。然而,对于像Either这样的效果,我们需要测试一下:

(Right(2).withLeft[Throwable]).tap(....)
def tap[U](f: A => U): A = {
    f(self)
    self
  }

关于如何在不复杂的情况下实现这一目标的任何建议,例如引入高级库等......

回答

想象一下你做了一些副作用:

def fetchUser(id: ID): F[User] // side effect - fetching from DB

def modifyUser(user: User): User // pure computation

def saveUser(user: User): F[Unit] // side effect - storing in DB

如果您提取,然后修改,然后尝试保存修改,会发生什么?

fetchUser(userID).map(modifyUser).flatMap(saveUser)

你会得到F[Unit]. User将被更新,但您将无法访问它。

因此,您可能认为可以执行以下操作:

fetchUser(userID).map(modifyUser).flatMap { modified =>
  saveUser(modified).map(_ => modified)
}

除非每次要触发不应影响返回值的计算时都这样做会很烦人。这就是为什么我们有flatTap

fetchUser(userID).map(modifyUser).flatTap(saveUser) // F[User]

它与 有什么不同tap

假设您的F= cats.effect.IO

两个都:

fetchUser(userID).map(modifyUser).flatTap(saveUser)

fetchUser(userID).map(modifyUser(_).tap(saveUser))

将具有相同的类型。

然而,最后一部分将是不同的值:

user =>
  saveUser(u).map(_ => u)
user =>
  saveUser(u) // lazy computation!!!
  user // saveUser value is discarded!!!

所以flatTap会确保saveUser计算是程序蓝图的一部分,同时.tap会创建一个计算配方并丢弃它而不将它组合到程序中。

典型的用例.tap是您希望执行可以安全丢弃返回值的操作,而不会破坏您的逻辑。通常,它Unit由立即产生副作用的操作返回:

// - we dont' want to return Unit
// - we can discard it and return original value
// - println is _eager_ so the side effects will happen immediately
//   and so we won't skip them if we discard the value
calculateSomething().tap { value =>
  println(value)
}
// each setX returns Unit so we couldn't chain them together
// (no .setA(...).setB(...)) but we can chain .taps
val value = new JavaObject()
  .tap(_.setA("value"))
  .tap(_.setB(1))

在日志记录的上下文中,您是否需要.tap.flatTap取决于您使用的日志记录解决方案:一种立即记录(Log4j、Slf4J、Scala Logging 等)或一种F[Unit]稍后返回以供评估的(Log4Cats)。类型应该引导你(你不能返回Unit.flatTap也不能.tap打开F[X]并期望接收X),但如果你有疑问,只需玩弄代码,看看不纯计算的行为与引用透明的计算行为有何不同。


以上是猫flatTap或scalatap的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>