Scala 为Try[T]创建一个方便的日志记录功能,但由于类型系统而被卡住

Scala 为Try[T]创建一个方便的日志记录功能,但由于类型系统而被卡住,scala,casting,type-conversion,scala-generics,Scala,Casting,Type Conversion,Scala Generics,我想知道是否可以使用Scala的类型系统来实现这一点 基本上,我想创建一个日志记录方法,它接受类型为Try[T]的结果,并根据结果是成功还是失败打印出一条稍有不同的消息 例如,签名可能看起来像 def logTry[T](what: Try[T], block: T => String): Unit 并且可以这样使用: val size: Try[(Int, Int)] = Try(getSizeAndTimestampFromDatabase()) logTry(size, e =&g

我想知道是否可以使用Scala的类型系统来实现这一点

基本上,我想创建一个日志记录方法,它接受类型为Try[T]的
结果,并根据
结果是
成功还是
失败打印出一条稍有不同的消息

例如,签名可能看起来像

def logTry[T](what: Try[T], block: T => String): Unit
并且可以这样使用:

val size: Try[(Int, Int)] = Try(getSizeAndTimestampFromDatabase())
logTry(size, e => "size is " + e._2 + " kb")
哪个会输出

大小为13KB
如果
大小
Success(x:Int)
,例如
Success(13)

错误:大小为(不可用)
if
size
if类型
Failure(t:Throwable)

棘手的部分是,如果
成功
,我们需要能够访问
Try
中的对象以打印到屏幕上;如果
失败
,我们需要打印所选的默认字符串(例如“(不可用)”),作为占位符。此外,它还必须使用非常通用的类型
T
Any
,其范围从简单的标量值到类的实例等

这个古怪函数的用例是,它将非常方便地以信息性的方式记录Try对象,而不会使代码与map/recover或match语句混淆

这是我想出来的骨架,当然最难的部分还没有弄清楚

def logTry[T](what: Try[T], block: T => String): Unit = {
  what match {
    case Success(res) => println(block(res))
    case Failure(t) => println(???) // how to do this
  }
}
这就是我要做的(如果我没有使用implicits将方法添加到
Try
):

编辑:在理解您的请求之后

def logTry(t: Try[Any], f: Any => String) {
    t match {
        case Success(e) => println(f(e))
        case Failure(e) => println(f(new Object {
            override def toString = "(not available)"
        }))
    }
}

val size: Try[Integer] = Try(getSizeFromDatabase())
logTry(size, s => s"Size is $s kb")
我不知道如何实现泛型类型t,但我认为这无论如何都不是一个好主意:客户机在打印时可以特别使用t的方法,但您希望它总是在应该打印来自t的信息的地方打印“不可用”,这并不是那么简单


另一个选项是创建一个特殊的格式化程序(想想
“大小是${…}kb”
),这样记录器就会知道如何替换它,但我不确定这是否是您想要的。

这里是解决方案,它不会强制您显式提供类型:

implicit class LogTry[T](attempt: Try[T]) {
  def log[E](comp: T => E, block: String => String, error: Throwable => String = _ => "(N/A)") =
    println(block(attempt map (comp andThen (_.toString)) recover { case ex => error(ex) } get))
}
把它当作

size.log(_._2, size => f"size is $size kb")


如果我们这样做,我们将强制函数的用户显式地处理成功和失败案例。此外,如果要避免重复,则必须将错误/成功消息写入两次,或者将其声明为单独的val。在my函数中,当值不可用时,我们提供一个合理的占位符。您提供了什么占位符?我懂了???处理一个错误。如果您只是想打印“error”,为什么不在原始实现中这样做呢?仅供参考,
Predef
中的有效方法???是“throw new NotImplementedError”的别名,我将其解释为“我不知道在这里做什么”。如果你想从外部接收字符串,你可以按照我下面@Odomontois的建议去做。我把它放在那里,因为我正在研究如何实现这个部分。我想要的输出应该是
size是13kb
error:size是(不可用)kb
。这不够简洁,想象一下如果我们可以这样做
logTry[(Int,Int)](size,e=>f“size是${e.\u 2}kb”)
并得到一个非常合理的输出,即
size是(N/a)kb
-不可用值将自动替换为“占位符”
size.log(_._2, size => f"size is $size kb")
size.log(_._2, size => f"size is $size kb", err => f"(not available because $err)")