2016-03-31 3 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 «s, как показано на выходе y печати.

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

Как подпись f «s остаются прежними:

(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 «s подпись имеет 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 Добавил импорт. –