Suppose I want to wrap code that can throw exceptions with a try-catch block that logs the exception and continues. Something like:
loggingExceptions {
// something dangerous
}
Ideally, I would like to use for logging the Logger defined on the calling object, if any (and if none, get a compile-time error). I'd love to define something like this:
def loggingExceptions[L <: { def logger: Logger }](work: => Unit)(implicit objectWithLogger: L): Unit = {
try {
work
} catch {
case t: Exception => objectWithLogger.logger.error(t.getMessage)
}
}
where objectWithLogger would somehow "magically" expand to "this" in client code. Is this (or a similar thing) possible?
It can in fact be done just as you want. The other answerers surrendered too quickly. No white flags!
package object foo {
type HasLogger = { def logger: Logger }
implicit def mkLog(x: HasLogger) = new {
def loggingExceptions(body: => Unit): Unit =
try body
catch { case ex: Exception => println(ex) }
}
}
package foo {
case class Logger(name: String) { }
// Doesn't compile:
// class A {
// def f = this.loggingExceptions(println("hi"))
// }
// 1124.scala:14: error: value loggingExceptions is not a member of foo.A
// def f = this.loggingExceptions(println("hi"))
// ^
// one error found
// Does compile
class B {
def logger = Logger("B")
def f = this.loggingExceptions(println("hi"))
def g = this.loggingExceptions(throw new Exception)
}
}
object Test {
def main(args: Array[String]): Unit = {
val b = new foo.B
b.f
b.g
}
}
// output
//
// % scala Test
// hi
// java.lang.Exception
Debilski's answer will work, but I'm not sure I see a good reason to use a structural type (i.e. { def logger: Logger }) here. Doing so will incur extra runtime overhead whenever logger is invoked, since the implementation of structural types relies on reflection. The loggingExceptions method is closely tied to logging, so I would just make it part of a Logging trait:
trait Logging {
def logger: Logger
final def loggingExceptions(body: => Unit) =
try body catch { case e: Exception => logger.error(e.getMessage) }
}
trait ConcreteLogging extends Logging {
val logger = // ...
}
object MyObject extends SomeClass with ConcreteLogging {
def main {
// ...
loggingExceptions {
// ...
}
}
}
You could add a trait to all classes which want to use def loggingExceptions and in this trait add a self-type which expects def logger: Logger being available.
trait LoggingExceptions {
this: { def logger: Logger } =>
def loggingExceptions(work: => Unit) {
try { work }
catch { case t: Exception => logger.error(t.getMessage) }
}
}
object MyObjectWithLogging extends OtherClass with LoggingExceptions {
def logger: Logger = // ...
def main {
// ...
loggingExceptions { // ...
}
}
}
Related
So I have defined a Scala object using the following code:
trait SomeTrait {
def someMethod(): Unit
}
package somepackage1 {
object Impl1 extends SomeTrait {
def someMethod(): Unit = { }
}
object Impl2 extends SomeTrait {
def someMethod(): Unit = { }
}
}
I want to access the object given its fully qualified name i.e. somepackage1.Impl2. A method something like following:
package object somewhereElse {
def getImpl(qualifiedName: String): SomeTrait = {
???
}
}
What should be the code as part of getImpl method?
Here is a solution that uses the Java Reflection API (you might want to use the newer Scala Reflection API instead):
trait SomeTrait {
def someMethod(): Unit
}
package somepackage1 {
object Impl1 extends SomeTrait {
def someMethod(): Unit = { println("impl 1") }
}
object Impl2 extends SomeTrait {
def someMethod(): Unit = { println("impl 2") }
}
}
package object somewhereElse {
def getImpl(qualifiedName: String): SomeTrait = {
val clazz = Class.forName(qualifiedName + "$")
clazz
.getField("MODULE$")
.get(clazz)
.asInstanceOf[SomeTrait]
}
}
object Demo {
def main(args: Array[String]): Unit = {
somewhereElse.getImpl("somepackage1.Impl2").someMethod()
}
}
It's quite similar to Dima's comment above, with two minor differences: note the '$' appended to class name, and also the .get(clazz) instead of just .get() (without the clazz, it throws java.lang.NoSuchFieldException: MODULE$).
When saved to file f.scala, and compiled and invoked using
scalac f.scala && scala Demo
it prints:
impl 2
Any idea how to get the following to work:
trait HttpTransport {
def doGet(str: String): String
}
trait CoreGet {
def GET(str: String)(implicit imp:String): List[String]
}
trait VerbGet extends CoreGet with HttpTransport {
override def GET(str: String)(implicit imp:String): List[String]= {
println("->VerbGet.GET")
val str1 = doGet(str)
// and more biz logic calls here
List(s"\nverb (biz logic) called url $str and got '${str1}'>")
}
}
// PlayGet {
implicit class ExtendCoreGet(coreGet: CoreGet) {
def GET[A](url: String)(implicit imp:String, imp2: List[A]): List[A]= {
println(s"->ExtendCoreGet.GET($url)")
val coreGetResult = coreGet.GET(url)
coreGetResult.flatMap(_ => imp2)
}
}
trait Play extends HttpTransport {
override def doGet(str: String): String = {
println("->Play.doGet")
s"\nPlay.doGet($str)>"
}
}
val client = new VerbGet with Play
client.GET("www.go.com")("hello", List("1")) //<-- does not compile
Compiler error:
too many arguments (2) for method GET: (implicit imp:
String)List[String]
You can play with the code here:
https://scastie.scala-lang.org/arminio/tN9NfdxGQUmusrNL0lJ78w
It looks like you are trying extend functionality of VerbGet. You need to fix two things:
1. ExtendCoreGet must extend AnyVal to add more methods.
2. You can add new functionality by adding new method say GET2 but you can't overload existing method. Renmaed your GET to GET2 or something meaningful.
ExtendCoreGet definition should be
implicit class ExtendCoreGet(val coreGet: CoreGet) extends AnyVal {
def GET2[A](url: String)(implicit imp:String, imp2: List[A]): List[A]= {
coreGet.GET(url).flatMap(_ => imp2)
}
}
I am writing a piece of code which I feel has become very convoluted.
I have a API which accepts an argument which is a trait. This trait can be implemented by many types. Furthermore, each of these classes need to be handled by a specialized processor.
For example, I have created a Trait below called Context which has two actual types of MobileContext and WebContext.
let's say that the MobileContext and WebContext are logged differently and we have specialized implementations in the form of ContextWriter[MobileContext] and ContextWriter[WebContext].
The requirement is that the method should be generic, but it should be able to dispatch the call to the right ContextWriter depending on the actual type of the trait.
Here is my code.
trait Context
case class WebContext(name: String) extends Context
case class MobileContext(name: String) extends Context
trait ContextWriter[T] {
def log(message: String, context: T) : Unit
}
object ContextWriterUtil {
def log[T](message: String, context: T)(implicit writer: ContextWriter[T]) = {
writer.log(message, context)
}
}
object ContextWriterImplicits {
implicit val webImpl = new ContextWriter[WebContext] {
override def log(message: String, context: WebContext) = println(s"I am in web context ${context} and the message is ${message}")
}
implicit val mobileImpl = new ContextWriter[MobileContext] {
override def log(message: String, context: MobileContext) = println(s"I am in mobile context ${context} and the message is ${message}")
}
implicit val baseImpl = new ContextWriter[Context] {
override def log(message: String, context: Context) = context match {
case s: WebContext => {
val writer = implicitly[ContextWriter[WebContext]]
writer.log(message, s)
}
case s: MobileContext => {
val writer = implicitly[ContextWriter[MobileContext]]
writer.log(message, s)
}
case _ => throw new Exception("don't understand this type")
}
}
}
import ContextWriterImplicits._
object MyApplication extends App {
// this is the generic method.
def call[T <: Context](message: String)(implicit context: T) = {
val actualContext = implicitly[Context]
ContextWriterUtil.log(message, actualContext)
}
def web() = {
implicit val webContext = WebContext("web")
call("I am calling the method")
}
def mobile() = {
implicit val mobileContext = MobileContext("mobile")
call("I am calling the method")
}
web()
mobile()
}
This works. But I feel its too verbose and unwieldy. I want to write this in a cleaner way.
TLDR: Remove inheritance from your code.
I don't see why you would need baseImpl: ContextWriter[Context], just delete this implicit and always ask for the more precise context. call becomes:
def call[T: ContextWriter](message: String)(implicit context: T) = {
ContextWriterUtil.log(message, context)
}
For that to work you need to update web and mobile to explicitly specify the type parameter. Even if there is a single instantiation of this type parameter that make the code compile, scalac is not able to figure that out:
def web() = {
implicit val webContext = WebContext("web")
call[WebContext]("I am calling the method")
}
def mobile() = {
implicit val mobileContext = MobileContext("mobile")
call[MobileContext]("I am calling the method")
}
You might be able to get ride of the explicit typing by combining Context and ContextWriter into a single implicit. For example, why not take an ìmplicit ContextWriter argument when you instanciate your Context, and be done with it?
I have trimmed my code down to the following. I am confused why I am getting a stack overflow between the two filter methods (one in my trait and one in my superclass)
object TestingOutTraits {
val TestHandler = new Object with MySuper with MyTrait {
override lazy val createdFilter = {
"second part"
}
}
def main(args: Array[String]) = {
val result : String = TestHandler.start()
System.out.println("result="+result)
}
}
trait MySuper {
protected def filter: String = {
"first part to->"
}
def start() = {
filter
}
}
trait MyTrait { self: MySuper =>
lazy val createdFilter = {
"override this"
}
protected override def filter: String = {
self.filter + createdFilter
}
}
This is scala 2.9. Any ideas what is going on here?
EDIT:
The stack trace makes no sense on how it jumps back and forth too(I should have included it in original post)...
at MyTrait$class.filter(TestingOutTraits.scala:34)
at TestingOutTraits$$anon$1.filter(TestingOutTraits.scala:4)
at MyTrait$class.filter(TestingOutTraits.scala:34)
at TestingOutTraits$$anon$1.filter(TestingOutTraits.scala:4)
thanks,
Dean
The call self.filter in MyTrait.filter invokes itself, leading to infinite recursion that blows the stack.
Instead, have MyTrait extend MySuper, and use super.filter:
trait MyTrait extends MySuper {
lazy val createdFilter = {
"override this"
}
protected override def filter: String = {
super.filter + createdFilter
}
}
Alternatively,
trait MySuper extends Filtered {
protected def filter: String = {
"first part to->"
}
def start() = {
filter
}
}
trait Filtered {
protected def filter: String
}
trait MyTrait extends Filtered {
lazy val createdFilter = {
"override this"
}
protected abstract override def filter: String = {
super.filter + createdFilter
}
}
then
val nope = new MyTrait { } // correctly DNC
and the OP
val TestHandler = new MySuper with MyTrait {
override lazy val createdFilter = {
"second part"
}
}
http://www.artima.com/pins1ed/traits.html#12.5
Well, I know why the infinite recursion though this seems like a linearization bug in scala and I am not sure how to work around it yet either :(.
hmmmm, so it turns out the compiler is sticking a filter method in my new Object for some reason. I found this out with the print:mixin on scalac like so
$ scalac -Xprint:mixin TestingOutTraits.scala
[[syntax trees at end of mixin]]// Scala source: TestingOutTraits.scala
package <empty> {
final object TestingOutTraits extends java.lang.Object with ScalaObject {
private[this] val TestHandler: MySuper = _;
<stable> <accessor> def TestHandler(): MySuper = TestingOutTraits.this.TestHandler;
def main(args: Array[java.lang.String]): Unit = {
val result: java.lang.String = TestingOutTraits.this.TestHandler().start();
java.this.lang.System.out.println("result=".+(result))
};
def this(): object TestingOutTraits = {
TestingOutTraits.super.this();
TestingOutTraits.this.TestHandler = {
new anonymous class TestingOutTraits$$anon$1()
};
()
}
};
abstract trait MySuper extends java.lang.Object with ScalaObject {
def filter(): java.lang.String;
def start(): java.lang.String
};
abstract trait MyTrait extends java.lang.Object with ScalaObject { self: MyTrait =>
def createdFilter(): java.lang.String;
override def filter(): java.lang.String
};
abstract trait MySuper$class extends {
def filter($this: MySuper): java.lang.String = "first part to->";
def start($this: MySuper): java.lang.String = $this.filter();
def /*MySuper$class*/$init$($this: MySuper): Unit = {
()
}
};
abstract trait MyTrait$class extends { self: MyTrait =>
def createdFilter($this: MyTrait): java.lang.String = "override this";
override def filter($this: MyTrait): java.lang.String = $this.$asInstanceOf[MySuper]().filter().+($this.createdFilter());
def /*MyTrait$class*/$init$($this: MyTrait): Unit = {
()
}
};
final class TestingOutTraits$$anon$1 extends java.lang.Object with MySuper with MyTrait {
override def filter(): java.lang.String = MyTrait$class.filter(TestingOutTraits$$anon$1.this);
def start(): java.lang.String = MySuper$class.start(TestingOutTraits$$anon$1.this);
override def createdFilter(): java.lang.String = "second part";
def this(): anonymous class TestingOutTraits$$anon$1 = {
TestingOutTraits$$anon$1.super.this();
MySuper$class./*MySuper$class*/$init$(TestingOutTraits$$anon$1.this);
MyTrait$class./*MyTrait$class*/$init$(TestingOutTraits$$anon$1.this);
()
}
}
I've made a Logging trait which encapsulates the details of a logging implementation, it's also nice and lazy so is efficient especially when a particular log level is not active.
/**
* A SLF4J based logging trait
*/
trait Log {
import org.slf4j.Logger
import org.slf4j.LoggerFactory
val loggedClazz: Class[_]
lazy val logger: Logger = LoggerFactory.getLogger(loggedClazz.getClass)
def logDebug(codeblock: => String) = {
if (logger.isDebugEnabled) {
logger.debug(codeblock)
}
}
def logError(codeblock: => String) = {
if (logger.isErrorEnabled) {
logger.error(codeblock)
}
}
def logInfo(codeblock: => String) = {
if (logger.isInfoEnabled) {
logger.info(codeblock)
}
}
def logWarn(codeblock: => String) = {
if (logger.isWarnEnabled) {
logger.warn(codeblock)
}
}
}
However it requires the class into which this trait is mixed-in to implement the following..
object MyServer extends Log {
val loggedClazz = MyServer.getClass
}
My question is, is it possible to somehow enable the Trait to know into which class it has been mixed into? Removing the need to do:
val loggedClazz = MyServer.getClass
SOLUTION: Following the provided feedback, I rewrote the class in the following manner.
/**
* A SLF4J based logging trait
*/
trait Log {
import org.slf4j.Logger
import org.slf4j.LoggerFactory
lazy val logger: Logger = LoggerFactory.getLogger(getClass)
def logDebug(codeblock: => String) = {
if (logger.isDebugEnabled) {
logger.debug(codeblock)
}
}
def logError(codeblock: => String) = {
if (logger.isErrorEnabled) {
logger.error(codeblock)
}
}
def logInfo(codeblock: => String) = {
if (logger.isInfoEnabled) {
logger.info(codeblock)
}
}
def logWarn(codeblock: => String) = {
if (logger.isWarnEnabled) {
logger.warn(codeblock)
}
}
}
Totally simple. When you do it right, first time ;)
You could replace val loggedClazz: Class[_] with val loggedClazz = getClass.
Your current code won't work as expected as the returned logger will always be for class Class[Class[_]], as you're calling getClass on a Class[_] object.
Use this instead:
lazy val logger: Logger = LoggerFactory.getLogger(getClass)
You may also want to have a look SLF4S, a thin wrapper around SLF4J, which is very similar to what you're doing.