是否可以通过“this”?作为 Scala 中的隐式参数?

发布于 2024-10-05 00:17:27 字数 510 浏览 4 评论 0原文

假设我想用一个记录异常并继续的 try-catch 块来包装可以引发异常的代码。类似的:

loggingExceptions {
  // something dangerous
}

理想情况下,我想用于记录调用对象上定义的 Logger(如果有的话)(如果没有,则会出现编译时错误)。我很想定义这样的东西:

def loggingExceptions[L <: { def logger: Logger }](work: => Unit)(implicit objectWithLogger: L): Unit = {
  try {
    work
  } catch {
    case t: Exception => objectWithLogger.logger.error(t.getMessage)
  }
}

其中 objectWithLogger 会以某种方式“神奇地”扩展到客户端代码中的“this”。这(或类似的事情)可能吗?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

不爱素颜 2024-10-12 00:17:27

事实上它可以按照你想要的方式完成。其他回答者太快投降了。没有白旗!

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

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
怪异←思 2024-10-12 00:17:27

Debilski 的回答会起作用,但我不确定我是否看到在这里使用结构类型(即 { def logger: Logger })的充分理由。每当调用 logger 时,这样做都会产生额外的运行时开销,因为结构类型的实现依赖于反射。 loggingExceptions 方法与日志记录密切相关,因此我只需将其作为 Logging 特征的一部分即可:

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 { 
         // ...
      }
   }
}

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 { 
         // ...
      }
   }
}
你怎么敢 2024-10-12 00:17:27

您可以向所有想要使用 defloggingExceptions 的类添加一个特征,并在这个特征中添加一个期望 def logger: Logger 可用的自我类型。

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 { // ...
    }
  }
}

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 { // ...
    }
  }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文