2013-05-07 2 views
15

Могу ли я повернуть метод, который принимает неявный параметр в функцию?Частично применяя функцию, которая имеет неявный параметр

trait Tx 

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

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

Я пытаюсь добиться следующего, предпочтительно, если я могу как-то заставить его работать с простым вызовом 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 
} 

я нашел this question, однако это не дело с насколько я вижу, от методов до функций.

ответ

6

Implicits работает только для методов. Но вы должны передать функцию withSelection. Вы можете обойти путем оборачивания метода в функции:

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

Его невозможно передать deleteObjects напрямую, потому что foo _ не работает для foo со списком неявного параметра определен.

4

Насколько я знаю, неявное разрешение должно иметь место на сайте использования и не может быть отброшено. Мой собственный момент разочарования заключался в том, что я пытался работать над распространением «ExecutionContext» в моем коде.

Один компромисс я рассматривал был:

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

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

'implicit' имеет место только внутри функции - мы должны идти на компромисс при вызове:

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

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

Другой компромисс - один, который я выбрал, и жалел:

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

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

Это изменяет использование с наивного (но de хряков):

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

Чтобы следующее:

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

В зависимости от контекста, это может быть терпимо. Вы можете добавить транзакцию'def, где я использовал'def async '.

Я сожалею об этом, так как это усложняет наследование и накладывает некоторые накладные расходы на распределение (хотя это должно быть удалено).

Подводя итог, вы должны придумать более явный поэтапный метод вызова вашей функции - менее элегантный, чем каррирование в одиночку.

+2

О вашем втором случае (например, FileCounter), вы не могли бы определить неявное преобразование 'implicit def counterToAsync (c: FileCounter): AsyncFileCounter = c.async', чтобы вы могли еще раз' counter.countFiles ("/") '? –

+0

Ницца! В моем случае неявное преобразование будет проиграно синхронными методами со сходными сигнатурами, которые существуют на содержащем объекте. Тем не менее, это, безусловно, хороший шаблонный способ! Мне действительно нужно проверить, что анализ escape действительно устраняет выделение кучи для объекта Async. – nadavwr

+0

Я бы сказал, что синхронные методы будут иметь другую подпись, так как они не вернут «Будущее [A]», а просто «A», поэтому компилятор вам это может сказать.Вы должны просто разграничить области, где вам нужен асинхронный вызов, и импортировать там неявное преобразование. Что касается исключения выделения кучи, я не могу делать ставку на это ... –