Можно ли передать это в качестве неявного параметра в Scala? - PullRequest
9 голосов
/ 24 ноября 2010

Предположим, я хочу обернуть код, который может генерировать исключения, с блоком 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» в клиентском коде. Возможно ли это (или похожая вещь)?

Ответы [ 3 ]

11 голосов
/ 24 ноября 2010

На самом деле это можно сделать так, как вы хотите.Другие ответчики сдались слишком быстро.Нет белых флагов!

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
4 голосов
/ 24 ноября 2010

Ответ Дебильски будет работать, но я не уверен, что вижу вескую причину для использования структурного типа (то есть { def logger: Logger }) здесь.Это вызовет дополнительные затраты времени выполнения при каждом вызове logger, поскольку реализация структурных типов зависит от отражения.Метод loggingExceptions тесно связан с ведением журнала, поэтому я бы просто включил его в черту ведения журнала:

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 { 
         // ...
      }
   }
}
3 голосов
/ 24 ноября 2010

Вы можете добавить черту ко всем классам, которые хотят использовать def loggingExceptions, и в эту черту добавить собственный тип, который ожидает, что 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 { // ...
    }
  }
}
...