【问题标题】:How to define a function that takes a function literal (with an implicit parameter) as an argument?如何定义一个将函数文字(带有隐式参数)作为参数的函数?
【发布时间】:2011-08-15 18:33:55
【问题描述】:

我希望能够在这些行上做一些事情(不会编译):

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 的版本是最好的。

【问题讨论】:

  • 这样做的动机是什么?您编写的代码块似乎为零值。您显式定义隐式,然后将其隐式传递给代码......为什么不首先显式传递它呢?我知道您正在尝试在这里做一件特定的事情-当然-但我认为您正在做的事情令人困惑……为什么不直接将对象传递进去?
  • @derek 这是loan pattern 在工作。我同意 logScope 代码可能不那么愚蠢。
  • 我知道这是贷款模式 - 我没有得到的是篮球。在这里使用operationOne(logger)operationTwo(logger) 似乎是合适的。
  • @derek 如果您必须在 50 个不同的地方使用 scope,这会变得有点烦人。 logScope 可以是 sessionScope、fileScope 或任何数量的其他 范围,其方法使用特定资源(无论是注入的还是其他方式)。不必每次都指定参数。

标签: scala parameters implicit function-literal


【解决方案1】:

在你的第二个例子中试试这个:

logScope(Logger()) { implicit logger =>
  operationOne
}

它应该可以正常工作。这里的逻辑是“隐式”是 inside 闭包的特定值的属性,而不是闭包接口的一部分。

【讨论】:

  • 确实如此!迄今为止最短的方法。
  • 我想没有比这更好的了。
【解决方案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 值(DynamicScopeLogger 代理)中提升对 currentLoggerVar.value 的访问权限允许我们保持日志记录方法不变。 这也意味着我们可以默认使用动态范围,并在需要时通过简单地定义一个本地 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    
    

    在上面的例子中,operationOnelogScope 的词法范围内被调用,所以我们可能期望得到消息"Customized Logger 1: Inside operationOne", 但是我们看到使用了默认记录器。这是因为Future.apply 的主体的执行被推迟,并在稍后发生在另一个线程上(在我们将变量Logger.currentLoggerVar 重置为其默认值之后)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-23
    • 2013-11-06
    • 2012-12-13
    • 1970-01-01
    相关资源
    最近更新 更多