Частично применяя функцию, которая имеет неявный параметр

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

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
}

Я нашел этот вопрос, однако он не касается снятия с методов до функций, насколько я могу видеть.

Ответы

Ответ 1

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

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

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

Ответ 2

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

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

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

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

"Явный" имеет место только внутри функции - нам приходится идти на компромисс при вызове:

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

Это изменяет использование с наивного (но желаемого):

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

К следующему:

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

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

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

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