2016-03-31 33 views
1

已知:使參數變懶而不更改函數簽名?

scala> def f(x: Int, y: Int): Int = 
    | if(x == 55) x else y 
f: (x: Int, y: Int)Int 

scala> def yFn: Int = {println("y"); 42} 
yFn: Int 

調用f(55, yFn)導致f的評價,由y打印輸出中。

scala> f(55, yFn) 
y 
res0: Int = 55 

f的簽名如何可以保持不變:

(Int, Int) => Int

y懶洋洋地評估沒有使y一個by-name參數?

+4

你不能,除非有一些允許它的宏觀魔法。即使有一種方法我強烈建議不要這樣做,因爲這意味着明確違反了簽名合同和語言規範。 –

+0

即使不推薦,我也有興趣看到這樣一個宏觀實現 - 爲了我自己的學習。 –

+0

我甚至無法設想一種方法來處理宏。 –

回答

2

談到f成宏將有效地做你想要什麼:

import language.experimental.macros 
import scala.reflect.macros.whitebox.Context 

object Macros { 
    def f(x: Int, y: Int): Int = macro fImpl 

    def fImpl(c: Context)(x: c.Expr[Int], y: c.Expr[Int]): c.Expr[Int] = { 
    import c.universe._ 
    c.Expr(q"if ($x == 55) $x else $y") 
    } 
} 

f的簽名具有Int,不=> Int,但調用f(55, yFn)將僅僅通過if (55 == 55) 55 else yFn所取代,這不會評價yFn

從這裏你可以看到,任何宏沒有看到它的參數結構有效地使用它們的名稱;如果你想確保一次參數被評估一次,你需要將它分配給一個局部變量:q"{ val x = $x; if (x == 55) x else $y }"

但是,如果你想f轉換成對象,而不是一個方法,這將丟失,因爲生成的類的apply方法不會是一個宏(它不能;一個宏不能實現抽象方法(或者覆蓋具體的方法))。

+0

嗨阿列克謝 - 感謝您的答案。你可以請包括必要的進口來使用這個代碼? –

+1

@KevinMeredith添加了導入。 –