2013-03-26 34 views
2

考慮以下功能:名副其實的惡意語義?

import java.util.concurrent.Callable; 

def callable[T](operation: =>T) : Callable[T] = { 
    new Callable[T] { 
    def call : T = operation 
    } 
} 

在REPL,這段代碼做什麼,我想:

scala> val myCallable = callable { 
    | println("Side effect!"); 
    | "Hi!" 
    | } 
myCallable: java.util.concurrent.Callable[String] = [email protected] 

scala> myCallable.call 
Side effect! 
res3: String = Hi! 

scala> myCallable.call 
Side effect! 
res4: String = Hi! 

的按姓名參數不計算,直到函數「叫」被調用,每次調用函數時都會重新評估。這是我想要的行爲。

但在spec,它說以下有關名稱參數:

「相應的說法是沒有的功能應用的角度進行評估,而是在函數中的每個應用進行評估。」

從這個描述,我不清楚我可以依靠我想要的行爲。 「在功能中使用什麼意思」是什麼?我怎麼知道這是指我的Callable被調用的點(有時在無限期的將來),而不是它被定義的點(非常「在函數內」)?

該代碼正在做我想要的。但是如果我確定這種行爲是可靠的,那麼我會更容易些,而不是在未來版本的scala中可能「修復」的錯誤。

+0

FWIW,這是它「應該」工作的方式,所以如果在規範中存在錯誤而不是實現。但我不確定該規範是否充分說明了這將會發生 - 特別是每次都運行「副作用」。 – 2013-03-26 12:03:56

回答

2

這不是一個錯誤 - 該行爲是按照預期的。您可以將「在函數中每次使用時評估」遞歸地視爲「在評估表達式時函數中的表達式中的每次使用時評估」。

2

「函數」是您傳遞參數的函數。這是什麼通道試圖要提醒你的是:

scala> def byName(arg: => String) = arg + arg 
byName: (arg: => String)java.lang.String 

scala> byName({println("hi") ; "foo" }) 
hi 
hi 
res0: java.lang.String = foofoo 

即你的副作用會發生在每次引用參數的時間。由於你只做過一次,這與你的案例沒有什麼關係(除了評估點,這是在功能內部,而不是在呼叫站點)。

1

爲了擴大以前的答案並闡明避免這種情況的方法,如果您只希望一次性評估函數中的值,您可以在函數內捕獲值。通過這樣做,您正在對「按名稱」參數進行評估,並且不止一次使用計算的值,而不是對相同表達式進行2次評估。

scala> def byName(arg: => String) = {val computedArg = arg; computedArg + computedArg} 
byName: (arg: => String)java.lang.String 

scala> byName({"println("hi") ; "foo" }) 
hi 
res0: java.lang.String = foofoo 

如果你需要做的是,在未來......

0

最後,獲得的方法參數(僅零和或一個評估),你可以這樣做的真正評價:

def m1(i: => Int) = { 
    lazy val li = i 
    ... 
    // any number of static or dynamic references to li 
    ... 
}