I wonder if this can be done using Scala's type system.
Basically, I want to make a logging method which accepts a result
of type Try[T]
and prints out a message that differs a bit depending whether result
is a Success
or a Failure
For example, the signature might look like
def logTry[T](what: Try[T], block: T => String): Unit
and can be used as such:
val size: Try[(Int, Int)] = Try(getSizeAndTimestampFromDatabase())
logTry(size, e => "size is " + e._2 + " kb")
which will output
size is 13 kb
if size
is Success(x: Int)
, e.g., Success(13)
or
error: size is (not available)
if size
if of type Failure(t: Throwable)
The tricky part is that we need to be able to access the object within the Try
for printing to the screen if it's a Success
, or print a chosen default string (e.g. "(not available)") if it's a Failure
as the placeholder. Also, it must work with a very general type T
or Any
that can range from a simple scalar value to an instance of a class, etc.
The use case for this eccentric function is that it will be very handy for logging a Try object in an informative way, without cluttering the code with map / recover or a match statement.
This is the skeleton that I have came up with, but of course the hard part hasn't been figured out yet.
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
}
}
Here the solution, which not forces you to explicitly supply types:
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))
}
use it as
size.log(_._2, size => f"size is $size kb")
or
size.log(_._2, size => f"size is $size kb", err => f"(not available because $err)")