如何在Scala中实现单体登录

如何在Scala中实现单体登录,scala,functional-programming,monads,Scala,Functional Programming,Monads,我经常想记录或打印一些东西而不改变它 看起来是这样的: val result = myResult // this could be an Option or a Future .map{r => info(s"the result is $r") r } 这三条线总是一样的 为了理解,这可以做得更好一些 但是我为第一个声明性版本寻找解决方案。它应该是这样的: val result = myResult .log(inf

我经常想记录或打印一些东西而不改变它

看起来是这样的:

val result = myResult // this could be an Option or a Future
      .map{r =>
        info(s"the result is $r")
        r
      }
这三条线总是一样的

为了理解,这可以做得更好一些

但是我为第一个声明性版本寻找解决方案。它应该是这样的:

val result = myResult
      .log(info(s"the result is ${_}"))
val compRes = "Logging Result" ~< {
    for {
      r <- myResult ~> (_.fold("The result is empty")(r => s"The result is $r")
    } yield r
  }
}

for (res <- compRes) {
  doSomethingTo(res)
}

logger.info(logging.run.written.shows)
2019-11-18 00:00:00,000 INFO Logging Result
  The result is XXX
这一个班轮可以放在链条上任何有地图的地方,比如:

val result = myResult
      .log(info(s"1. ${_}"))
      .filter(_ > 1)
      .log(info(s"2. ${_}"))
      ...

如何做到这一点?如果可能,无需功能库

好的,所以我决定对此进行讨论,我想收回我对

也许您可以定义一个隐式类,它有一个作用于产品的方法“log”

我相信未来和所有的单子(集合,选项)都有一个共同的祖先,结果证明我错了。我有以下解决方案,不使用猫。除了前面提到的“flatTap”之外,这可以用一种更漂亮的方式在猫身上实现,并可能用cats.Ref或其他东西修饰

Future是这里显而易见的大纲视图,但随着更多例外情况的出现,您可能需要扩展此对象

import scala.concurrent._ 
import ExecutionContext.Implicits.global

object MonadicConv { 
  implicit class MonadicLog[+B <: Product, A] (val u: B){
    def log(l: String, args: List[A] = List()): B = {
      println("logging")
      println(u)
      println(l)
      u
    }  
  }



 implicit class FutureLog[T, A](val u: Future[T]){
    def log(l: String, args: List[A] = List()) : Future[T] = {
      println("logging")
      println(u)
      println(l)
      u
    }
  }
}
这是打印出来的

logging
Some(5)
option test
logging
List(Some(5), Some(10), Some(1))
list test
logging
List(5, 10, 1)
flat test
logging
List(5, 10)
filter test
logging
Future(Success(List(Some(5), Some(10), Some(1))))
future test
logging
Future(<not completed>)
incomplete test
logging
List(5, 10)
inner future test
日志记录
一些(5)
选项测试
登录中
清单(一些(5),一些(10),一些(1))
列表测试
登录中
清单(5、10、1)
平面试验
登录中
名单(5、10)
过滤试验
登录中
未来(成功(列表(一些(5),一些(10),一些(1)))
未来测试
登录中
未来()
不完全检验
登录中
名单(5、10)
内部未来测试
斯卡斯蒂版本


正如我所提到的,这确实是猫在这一点上降落的地方。这是我在核心Scala中所能想到的最好的方法。出于您的目的,最好使用treelog。它将日志记录过程和值转换为
描述计算的单子:

import treelog.LogTreeSyntaxWithoutAnnotations._
val result: DescribedComputation[YourValueType] = myResult ~> (_.fold("The result is empty")(r => s"The result is $r")
通常从描述的计算中减去该值,用于理解:

for {
  res <- result
} {
  doSomethingTo(res)
}

仅供参考ZIO很好地提供了这一功能

  /**
   * Returns an effect that effectfully "peeks" at the success of this effect.
   *
   * {{{
   * readFile("data.json").tap(putStrLn)
   * }}}
   */
  final def tap[R1 <: R, E1 >: E](f: A => ZIO[R1, E1, Any]): ZIO[R1, E1, A] = self.flatMap(new ZIO.TapFn(f))

有趣的。。。也许你可以定义一个隐式类,它有一个作用于产品的方法“log”?这可以在使用cats时使用。@Marth我认为
flatTap
不适合链接,正如OP在上一个示例中所显示的那样。我打算建议使用新的(Scala 2.13)
tapEach()
,但它在
未来的
上不可用。我想知道为什么。也许在下一个版本中?@jwvh当然很好奇为什么期货被区别对待。我将把这个带到/贡献者那里,看看是否有人对此有解释谢谢,我必须为Iterable添加一个版本,因为该产品只包括Try和Option。是的。列表是一个产品,但我忘了它是一个特例。希望Scala 3引入联合类型后,我们可以进一步简化它
  /**
   * Returns an effect that effectfully "peeks" at the success of this effect.
   *
   * {{{
   * readFile("data.json").tap(putStrLn)
   * }}}
   */
  final def tap[R1 <: R, E1 >: E](f: A => ZIO[R1, E1, Any]): ZIO[R1, E1, A] = self.flatMap(new ZIO.TapFn(f))
  /**
   * Returns an effect that effectfully "peeks" at the failure of this effect.
   * {{{
   * readFile("data.json").tapError(logError(_))
   * }}}
   */
  final def tapError[R1 <: R, E1 >: E](f: E => ZIO[R1, E1, Any]): ZIO[R1, E1, A]
   myDangerousZioFunction
      .tapError(e => putStrLn(s"Server Exception: $e"))
      .tap(r => putStrLn(s"Result is $r"))
      ....