Параметр конструктора вызова по имени в неявном классе

Следующий код не компилируется. Желательно иметь параметр конструктора call-by-name в неявном классе, как показано здесь,

def f(n: Int) = (1 to n) product

implicit class RichElapsed[A](val f: => A) extends AnyVal {

  def elapsed(): (A, Double) = {
    val start = System.nanoTime()
    val res = f
    val end = System.nanoTime()

    (res, (end-start)/1e6)
  }

}

где звонок

val (res, time) = f(3).elapsed
res: Int = 6
time: Double = 123.0

Эта ошибка сообщается в REPL,

<console>:1: error: `val' parameters may not be call-by-name
       implicit class RichElapsed[A](val f: => A) extends AnyVal {

Таким образом, чтобы спросить, как RichElapsed класс может быть реорганизован.

Заранее спасибо.

2 ответа

Решение

Решение Питера Шмитца просто отбросить val (вместе с надеждой на поворот RichElapsed в класс значения), безусловно, самая простая и наименее навязчивая вещь, чтобы сделать.

Если вы действительно чувствуете, что вам нужен класс значений, другая альтернатива это:

class RichElapsed[A](val f: () => A) extends AnyVal {

  def elapsed(): (A, Double) = {
    val start = System.nanoTime()
    val res = f()
    val end = System.nanoTime()

    (res, (end-start)/1e6)
  }
}

implicit def toRichElapsed[A]( f: => A ) = new RichElapsed[A](() => f )

Обратите внимание, что при использовании класса значений, как указано выше, можно удалить экземпляр временного RichElapsed Например, все еще продолжается некоторая упаковка (как с моим решением, так и с решением Питера Шмитца). А именно, тело передано по имени как f заключен в экземпляр функции (в случае Питера Шмитца это не проявляется в коде, но все равно произойдет под капотом). Если вы хотите удалить эту упаковку, я думаю, что единственным решением будет использование макроса.

Сделать это без val как требует сообщения об ошибках, а затем вы также должны отказаться от AnyVal поскольку у класса значений должен быть ровно один публичный val:

implicit class RichElapsed[A](f: => A)
Другие вопросы по тегам