簡體   English   中英

如何定義一個 function 文字(帶有隱式參數)作為參數的 function?

[英]How to define a function that takes a function literal (with an implicit parameter) as an argument?

我希望能夠在這些行上做一些事情(不會編譯):

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
    }

我認為該語言目前不允許這樣的結構,但是,有什么建議或解決方法可以達到類似的結果嗎?


次要更新:我用上述代碼的略微擴展版本創建了一個要點,並嘗試了幾次模擬這種文字。 到目前為止,CheatEx 的版本是最好的。

在你的第二個例子中試試這個:

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

它應該可以正常工作。 這里的邏輯是“隱式”是閉包特定值的屬性,而不是閉包接口的一部分。

另一種解決方案是依靠動態 scope 模式而不是隱式參數。 實際上,您甚至可以將兩者結合起來,如下所示:

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的訪問,允許我們保持日志記錄方法不變。 這也意味着我們可以默認使用動態 scope ,並在需要時通過簡單地定義一個本地 Logger 隱式來覆蓋此行為,然后優先於DynamicScopeLogger

主要缺點是:

  • 根據速度要求,可能會太慢:訪問線程本地存儲是有代價的,包括(但不限於)在 map 中查找線程局部變量。

  • 這取決於詞法作用域與執行順序相匹配的事實(通常是這種情況,但並非總是如此)。 一旦不再這樣,你就會遇到麻煩。 例如,當在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 的詞法logScope中被調用,所以我們可能期望得到消息"Customized Logger 1: Inside operationOne" ,但是我們看到使用了默認的記錄器。 這是因為Future.apply的主體的執行被延遲並稍后發生在另一個線程上(在我們將變量Logger.currentLoggerVar重置為其默認值之后)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM