登录Scala时如何保持返回值 [英] how to keep return value when logging in scala
问题描述
在Java中编程时,我总是记录输入参数和方法的返回值,但是在scala中,方法的最后一行是返回值.所以我必须做类似的事情:
def myFunc() = {
val rs = calcSomeResult()
logger.info("result is:" + rs)
rs
}
为了简单起见,我编写了一个实用程序:
class LogUtil(val f: (String) => Unit) {
def logWithValue[T](msg: String, value: T): T = { f(msg); value }
}
object LogUtil {
def withValue[T](f: String => Unit): ((String, T) => T) = new LogUtil(f).logWithValue _
}
然后我将其用作:
val rs = calcSomeResult()
withValue(logger.info)("result is:" + rs, rs)
它将记录该值并返回它.它对我有用,但似乎很奇怪.因为我是一个古老的Java程序员,但是对scala还是陌生的,所以我不知道在scala中是否有更惯用的方法来完成此操作.
感谢您的帮助,现在我使用romusz推荐的Kestrel组合器创建了一个更好的工具
object LogUtil {
def kestrel[A](x: A)(f: A => Unit): A = { f(x); x }
def logV[A](f: String => Unit)(s: String, x: A) = kestrel(x) { y => f(s + ": " + y)}
}
我添加了f参数,以便可以通过slf4j将它传递给记录器,并且测试用例为:
class LogUtilSpec extends FlatSpec with ShouldMatchers {
val logger = LoggerFactory.getLogger(this.getClass())
import LogUtil._
"LogUtil" should "print log info and keep the value, and the calc for value should only be called once" in {
def calcValue = { println("calcValue"); 100 } // to confirm it's called only once
val v = logV(logger.info)("result is", calcValue)
v should be === 100
}
}
您正在寻找的被称为Kestrel组合器(K组合器):Kxy = x
.在返回传递给它的值时,您可以执行各种副作用操作(不仅是日志记录).阅读 https://github.com/raganwald/homoiconic/blob/master /2008-10-29/kestrel.markdown#readme
在Scala中,最简单的实现方法是:
def kestrel[A](x: A)(f: A => Unit): A = { f(x); x }
然后,您可以将打印/记录功能定义为:
def logging[A](x: A) = kestrel(x)(println)
def logging[A](s: String, x: A) = kestrel(x){ y => println(s + ": " + y) }
并像这样使用它:
logging(1 + 2) + logging(3 + 4)
您的示例函数变成了单行代码:
def myFunc() = logging("result is", calcSomeResult())
如果您更喜欢OO表示法,则可以使用其他答案中所示的隐式表示法,但是这种方法的问题在于,每次您要记录某些内容时都会创建一个新对象,如果这样做,可能会导致性能下降经常.但是为了完整起见,它看起来像这样:
implicit def anyToLogging[A](a: A) = new {
def log = logging(a)
def log(msg: String) = logging(msg, a)
}
使用方式如下:
def myFunc() = calcSomeResult().log("result is")
When programming in java, I always log input parameter and return value of a method, but in scala, the last line of a method is the return value. so I have to do something like:
def myFunc() = {
val rs = calcSomeResult()
logger.info("result is:" + rs)
rs
}
in order to make it easy, I write a utility:
class LogUtil(val f: (String) => Unit) {
def logWithValue[T](msg: String, value: T): T = { f(msg); value }
}
object LogUtil {
def withValue[T](f: String => Unit): ((String, T) => T) = new LogUtil(f).logWithValue _
}
Then I used it as:
val rs = calcSomeResult()
withValue(logger.info)("result is:" + rs, rs)
it will log the value and return it. it works for me,but seems wierd. as I am a old java programmer, but new to scala, I don't know whether there is a more idiomatic way to do this in scala.
thanks for your help, now I create a better util using Kestrel combinator metioned by romusz
object LogUtil {
def kestrel[A](x: A)(f: A => Unit): A = { f(x); x }
def logV[A](f: String => Unit)(s: String, x: A) = kestrel(x) { y => f(s + ": " + y)}
}
I add f parameter so that I can pass it a logger from slf4j, and the test case is:
class LogUtilSpec extends FlatSpec with ShouldMatchers {
val logger = LoggerFactory.getLogger(this.getClass())
import LogUtil._
"LogUtil" should "print log info and keep the value, and the calc for value should only be called once" in {
def calcValue = { println("calcValue"); 100 } // to confirm it's called only once
val v = logV(logger.info)("result is", calcValue)
v should be === 100
}
}
What you're looking for is called Kestrel combinator (K combinator): Kxy = x
. You can do all kinds of side-effect operations (not only logging) while returning the value passed to it. Read https://github.com/raganwald/homoiconic/blob/master/2008-10-29/kestrel.markdown#readme
In Scala the simplest way to implement it is:
def kestrel[A](x: A)(f: A => Unit): A = { f(x); x }
Then you can define your printing/logging function as:
def logging[A](x: A) = kestrel(x)(println)
def logging[A](s: String, x: A) = kestrel(x){ y => println(s + ": " + y) }
And use it like:
logging(1 + 2) + logging(3 + 4)
your example function becomes a one-liner:
def myFunc() = logging("result is", calcSomeResult())
If you prefer OO notation you can use implicits as shown in other answers, but the problem with such approach is that you'll create a new object every time you want to log something, which may cause performance degradation if you do it often enough. But for completeness, it looks like this:
implicit def anyToLogging[A](a: A) = new {
def log = logging(a)
def log(msg: String) = logging(msg, a)
}
Use it like:
def myFunc() = calcSomeResult().log("result is")
这篇关于登录Scala时如何保持返回值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!