2013-05-07 19 views
15

È possibile trasformare un metodo che utilizza un parametro implicito in una funzione?Applicazione parziale di una funzione con un parametro implicito

trait Tx 

def foo(bar: Any)(implicit tx: Tx) {} 

foo _ // error: could not find implicit value for parameter tx: Tx 

sto cercando di raggiungere i seguenti, preferibilmente se posso in qualche modo farlo funzionare con la chiamata pianura withSelection(deleteObjects):

trait Test {  
    def atomic[A](fun: Tx => A): A 

    def selection: Iterable[Any] 

    def withSelection(fun: Iterable[Any] => Tx => Unit) { 
    val sel = selection 
    if (sel.nonEmpty) atomic { implicit tx => 
     fun(sel)(tx) 
    } 
    } 

    object deleteAction { 
    def apply() { 
     withSelection(deleteObjects) // ! 
    } 
    } 

    def deleteObjects(xs: Iterable[Any])(implicit tx: Tx): Unit 
} 

ho trovato this question, tuttavia non si tratta con il passando da metodi a funzioni per quanto posso vedere.

risposta

6

Gli impliciti funzionano solo per i metodi. Ma devi passare una funzione a withSelection. È possibile ottenere intorno avvolgendo il metodo in una funzione:

withSelection(a => b => deleteObjects(a)(b)) 

La sua impossibile passare deleteObjects direttamente a causa foo _ non funziona per un foo con una lista di parametro implicito definito.

4

In base alle mie conoscenze, la risoluzione implicita deve avvenire nel sito di utilizzo e non può essere eliminata. Il mio momento di delusione è stato quando cercavo di aggirare la proliferazione di "ExecutionContext" nel mio codice.

Un compromesso Ho preso in considerazione era:

type Async[A] = ExecutionContext => Future[A] 

def countFiles(root: String): Async[Int] = implicit ec => 
    // ... 

L''implicit' detiene solo all'interno della funzione - abbiamo scendere a compromessi sulla invocazione:

implicit class AsyncExt[A](async: Async[A]) { 
    def invoke()(implicit ec: ExecutionContext) = async(ec) 
} 

implicit val ec = ... 
countFiles("/").invoke() 

Un altro compromesso - il uno che ho scelto e vissuto per rimpiangere:

class AsyncFileCounter(ec: ExecutionContext) { 
    def countFiles(root: String): Future[A] = ... 
} 

class FileCounter { 
    def async(implicit ec: ExecutionContext) = new AsyncFileCounter(ec) 
} 

Questo modifica l'utilizzo dall'ingenuo (ma de SIRED):

implicit val ec = ... 
val counter = new FileCounter 
counter.countFiles("/") // <-- nope 

Al seguente:

implicit val ec = ... 
val counter = new FileCounter 
counter.async.countFiles("/") // yep! 

A seconda del contesto, questo potrebbe essere sopportabile. Potresti aggiungere un'def transactional 'dove ho usato'def async'.

Tuttavia, mi rammarico di questo, poiché complica l'ereditarietà e causa un sovraccarico di allocazione (anche se dovrebbe essere JIT via).

La conclusione è che dovrai escogitare un metodo più esplicito e frammentario per invocare la tua funzione, una che è meno elegante della semplice ricerca.

+2

Informazioni sul secondo caso (ad es. FileCounter), non è possibile definire una conversione implicita 'implicit def counterToAsync (c: FileCounter): AsyncFileCounter = c.async' in modo da poter nuovamente" counter.countFiles ("/") '? –

+0

Bello! Nel mio caso la conversione implicita sarà sconfitta da metodi sincroni con firme simili che esistono sull'oggetto contenitore. Tuttavia, è sicuramente un buon rimedio per la caldaia! Ho davvero bisogno di verificare che l'analisi di escape elimini davvero l'allocazione dell'heap per l'oggetto Async, comunque. – nadavwr

+0

Direi che i metodi sincroni avrebbero una firma diversa, dal momento che non restituiranno un 'Future [A]' ma semplicemente un 'A', quindi il compilatore può dirlo.Dovresti semplicemente delimitare gli ambiti in cui hai bisogno della chiamata asincrona e importare lì la conversione implicita. Per quanto riguarda l'allocazione dell'heap, non posso scommettere su questo ... –