2011-01-10 3 views
7

Sto provando a scrivere una libreria di misurazioni delle prestazioni per Scala. La mia idea è di contrassegnare in modo trasparente le sezioni in modo da poter raccogliere i tempi di esecuzione. Sfortunatamente non ero in grado di piegare il compilatore alla mia volontà.Come si crea una funzione parziale con generici in scala?

Un esempio certamente forzato di quello che ho in 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) 
} 

Ora, questo è il più lontano ho ottenuto:

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

Utilizzando la funzione time lavora direttamente e il compilatore utilizza correttamente la restituire il tipo della funzione anonima per digitare la funzione "orario":

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

grande come sembra, questo modello ha una serie di carenze:

  • costringe l'utente a riutilizzare lo stesso simbolo a ogni invocazione
  • rende più difficile fare cose più avanzate come predefiniti timer a livello di progetto
  • non permette la libreria per inizializzare una volta una struttura di dati per 'timerBigString

Quindi qui si tratta mkTimer, che mi permettesse di applicare parzialmente la funzione di tempo e riutilizzarlo. Io uso mkTimer come questo:

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

Ma ottengo un errore di compilazione:

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

ottengo lo stesso errore se InLine l'accattivarsi:

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

Questo funziona se lo faccio val timerBigString = time('timerBigString) (_: String), ma questo non è quello che voglio. Mi piacerebbe posticipare la digitazione della funzione parzialmente applicata fino all'applicazione.

Concludo che il compilatore sta decidendo il tipo di ritorno della funzione parziale quando lo creo per la prima volta, scegliendo "Nothing" perché non può fare una scelta più informata.

Quindi immagino che quello che sto cercando sia una sorta di late-binding della funzione parzialmente applicata. C'è un modo per fare questo? O forse c'è un percorso completamente diverso che potrei seguire?

Bene, grazie per aver letto fin qui

-teo

risposta

7

Il modello al solito quando si vuole farmaci generici "pigri" è quello di utilizzare una classe con un metodo di applicare

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

Spot on! Grazie. –