2011-01-10 17 views
7

Estoy tratando de escribir una biblioteca de medidas de rendimiento para Scala. Mi idea es "marcar" secciones de forma transparente para que se pueda recopilar el tiempo de ejecución. Lamentablemente, no fui capaz de doblar el compilador a mi voluntad.¿Cómo creo una función parcial con genéricos en scala?

Un ejemplo es cierto artificial de lo que tengo en mente:

// 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) 
} 

Ahora bien, esto es lo más lejos que tengo:

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) 
    } 
} 

Uso de la función time trabaja directamente y el compilador utiliza correctamente el tipo de retorno de la función anónima para escribir la función 'tiempo':

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

grande como parece, este modelo tiene una serie de deficiencias:

  • obliga al usuario a volver a utilizar el mismo símbolo en cada invocación
  • hace que sea más difícil hacer cosas más avanzadas como predefinidos temporizadores a nivel de proyecto
  • no permite la biblioteca para inicializar una vez a la estructura de datos para 'timerBigString

Así que aquí se trata mkTimer, que me permitirá aplicar parcialmente la función de tiempo y volver a utilizarlo. Yo uso mkTimer así:

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

Pero consigo un error del compilador:

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

me sale el mismo error si Inline la currificación:

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

Esto funciona si lo hago val timerBigString = time('timerBigString) (_: String), pero esto no es lo que quiero. Me gustaría diferir el tipeo de la función parcialmente aplicada hasta la aplicación.

llego a la conclusión de que el compilador es decidir el tipo de retorno de la función parcial cuando cree por primera vez, eligiendo "nada" porque no puede tomar una decisión mejor informada.

así que supongo que lo que estoy buscando es una especie de enlace tardío de la función aplicada parcialmente. ¿Hay alguna manera de hacer esto? O tal vez hay un camino completamente diferente que podría seguir?

Bueno, gracias por leer hasta aquí

-teo

Respuesta

7

El patrón habitual cuando se quiere genéricos "perezosas" es el uso de una clase con un método de aplicar

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

el clavo! Gracias. –

Cuestiones relacionadas