我希望能够在这些方面做一些事情(不会编译):
def logScope(logger:Logger)(operation: (implicit l:Logger) => Unit) {/* code */ operation(logger) /* code */}
def operationOne(implicit logger:Logger) {/**/}
def operationTwo(implicit logger:Logger) {/**/}
然后像这样使用它:
logScope(new ConsoleLogger){logger =>
operationOne
operationTwo
}
但是最接近我的工作解决方案是:
def logScope(logger:Logger)(operation: Logger => Unit) {/* code */ operation(logger) /* code */}
def operationOne(implicit logger:Logger) {/**/}
def operationTwo(implicit logger:Logger) {/**/}
/* other code */
logScope(new ConsoleLogger){logger =>
implicit val l = logger
operationOne
operationTwo
}
我认为这种语言目前不允许这样的结构,但仍然有任何建议或变通方法可以达到类似的效果吗?
次要更新:我created a gist稍微扩展了上述代码的版本,并试图模拟这种文字。截至目前,CheatEx的版本是最好的版本。
答案 0 :(得分:6)
在你的第二个例子中试试这个:
logScope(Logger()) { implicit logger =>
operationOne
}
应该可以正常工作。这里的逻辑是'implicit'是闭包内特定值的属性,而不是闭包接口的一部分。
答案 1 :(得分:2)
另一种解决方案是依赖动态范围模式而不是隐式参数。 你甚至可以将两者结合起来,如下所示:
import scala.util.DynamicVariable
object Logger {
val defaultLogger = new ConsoleLogger( "DEFAULT: %s" )
val currentLoggerVar = new DynamicVariable[Logger]( defaultLogger )
implicit object DynamicScopeLogger extends Logger {
def log( msg: Any* ) {
currentLoggerVar.value.log( msg: _* )
}
}
}
trait Logger {
def log( msg: Any* )
}
class ConsoleLogger( val pattern: String ) extends Logger {
def log( msg: Any* ) { println( pattern.format( msg: _* ) ) }
}
def logScope[T](logger: Logger)( operation: => T ): T = {
Logger.currentLoggerVar.withValue( logger )( operation )
}
def operationOne(implicit logger: Logger) { logger.log( "Inside operationOne" ) }
def operationTwo(implicit logger: Logger) { logger.log( "Inside operationTwo" ) }
def operationThree(implicit logger: Logger) { logger.log( "Inside operationThree" ) }
def operationFour(implicit logger: Logger) { logger.log( "Inside operationFour" ) }
用法示例:
operationOne
logScope(new ConsoleLogger("Customized Logger 1: %s")){
operationTwo
logScope(new ConsoleLogger("Customized Logger 2: %s")){
operationThree
}
operationFour
}
结果是:
DEFAULT: Inside operationOne
Customized Logger 1: Inside operationTwo
Customized Logger 2: Inside operationThree
Customized Logger 1: Inside operationFour
当前记录器隐式传递“越界”(我们只使用全局(和线程本地)变量来存储当前记录器)。
我们每个人都不会在方法签名中的任何地方提及Logger
,并直接调用currentLoggerVar.value
。
在默认的隐式Logger值(currentLoggerVar.value
代理)中提升对DynamicScopeLogger
的访问权限允许我们保持不变的日志记录方法。
这也意味着我们可以默认使用动态作用域,并在需要时通过简单地定义隐式的本地Logger来覆盖此行为,然后将优先于DynamicScopeLogger
。
主要缺点是:
根据速度要求,可能太慢:访问线程本地存储有成本,包括(但不限于)在线程局部变量的映射中查找。
这依赖于词法作用域与执行顺序相匹配的事实(通常是这种情况,但并非总是如此)。一旦情况不再如此,你就会遇到麻烦。
例如,在scala.concurrent.Future
(或简称为Future.apply
)上调用map或flatMap时,map / flatMap的主体可能会在另一个线程中执行,因此正文不一定会使用预期的记录器:
scala>import scala.concurrent.Future
import scala.concurrent.Future
scala>import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.ExecutionContext.Implicits.global
scala>logScope(new ConsoleLogger("Customized Logger: %s")){
| Future{ operationOne }
|}
DEFAULT: Inside operationOne
res5: scala.concurrent.Future[Unit] = scala.concurrent.impl.Promise$DefaultPromise@1a38913
在上面的示例中,operationOne
被称为logScope
的词汇范围,因此我们可能会收到消息"Customized Logger 1: Inside operationOne"
,
但是我们看到使用了默认记录器。这是因为Future.apply
的主体的执行被推迟并在稍后的另一个线程上发生(在我们将变量Logger.currentLoggerVar
重置为其默认值之后)。