Как мне создать частичную функцию с обобщениями в Scala? - PullRequest
7 голосов
/ 10 января 2011

Я пытаюсь написать библиотеку измерений производительности для Scala.Моя идея состоит в том, чтобы прозрачно «пометить» разделы, чтобы можно было собрать время выполнения.К сожалению, я не смог подчинить компилятор своей воле.

Якобы придуманный пример того, что я имею в виду:

// generate a timing function
val myTimer = mkTimer('myTimer) 

// see how the timing function returns the right type depending on the
// type of the function it is passed to it
val act = actor {
    loop { 
        receive {

            case 'Int =>
                val calc = myTimer { (1 to 100000).sum }
                val result = calc + 10 // calc must be Int
                self reply (result)

            case 'String =>
                val calc = myTimer { (1 to 100000).mkString }
                val result = calc + " String" // calc must be String
                self reply (result)
}

Теперь это самое дальнее, что я получил:

trait Timing {
   def time[T <: Any](name: Symbol)(op: => T) :T = {
      val start = System.nanoTime
      val result = op
      val elapsed = System.nanoTime - start
      println(name + ": " + elapsed) 
      result
  }

  def mkTimer[T <: Any](name: Symbol) : (() => T) => () => T = {
      type c = () => T
      time(name)(_ : c)
  }
}

Использование функции time напрямую работает, и компилятор правильно использует тип возврата анонимной функции для ввода функции 'time':

val bigString = time('timerBigString) {
    (1 to 100000).mkString("-")
}
println (bigString)

Как ни странно,у этого шаблона есть ряд недостатков:

  • заставляет пользователя повторно использовать один и тот же символ при каждом вызове
  • затрудняет выполнение более сложных операций, таких как предопределенные таймеры уровня проекта
  • не позволяет библиотеке один раз инициализировать структуру данных для 'timerBigString

Так что здесь идет mkTimer, который позволил бы мне частично применить функцию времени и использовать ее повторно.Я использую mkTimer следующим образом:

val myTimer = mkTimer('aTimer)
val myString= myTimer {
    (1 to 100000).mkString("-")
}
println (myString)

Но я получаю ошибку компилятора:

error: type mismatch;
found   : String
required: () => Nothing
(1 to 100000).mkString("-")

Я получаю ту же ошибку, если встроить каррирование:

val timerBigString = time('timerBigString) _ 
val bigString = timerBigString  {
    (1 to 100000).mkString("-")
}
println (bigString)

Это работает, если я делаю val timerBigString = time('timerBigString) (_: String), но это не то, что я хочу.Я хотел бы отложить ввод частично примененной функции до применения.

Я пришел к выводу, что компилятор решает тип возвращаемого значения частичной функции при первом ее создании, выбирая «Nothing», потому что он не можетсделайте более осознанный выбор.

Так что я думаю, что я ищу что-то вроде позднего связывания частично примененной функции.Есть какой-либо способ сделать это?Или, может быть, есть совершенно другой путь, по которому я мог бы пойти?

Ну, спасибо, что прочитали это далеко

-teo

1 Ответ

8 голосов
/ 10 января 2011

Обычный шаблон, когда вам нужны "ленивые" обобщения, - это использование класса с методом apply

class Timer(name: Symbol) {
  def apply[T](op: => T) = time(name)(op)
}
def mkTimer(name: Symbol) = new Timer(name)
...