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

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

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
}

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


person 0__    schedule 07.05.2013    source источник


Ответы (2)


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

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

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

person Martin Ring    schedule 07.05.2013

Насколько мне известно, неявное разрешение должно происходить на месте использования и не может быть удалено. Мой собственный момент разочарования наступил, когда я пытался обойти распространение «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 transactional», где я использовал «def async».

Однако я сожалею об этом, так как это усложняет наследование и влечет за собой некоторые накладные расходы (хотя это следует убрать JIT).

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

person nadavwr    schedule 07.05.2013
comment
Что касается вашего второго случая (например, FileCounter), не могли бы вы определить неявное преобразование implicit def counterToAsync(c: FileCounter): AsyncFileCounter = c.async, чтобы вы могли еще раз counter.countFiles("/") ? - person pagoda_5b; 08.06.2013
comment
Хороший! В моем случае неявное преобразование будет побеждено синхронными методами с аналогичными сигнатурами, которые существуют в содержащем объекте. Тем не менее, это определенно хороший шаблонный обходной путь! Мне действительно нужно убедиться, что escape-анализ действительно устраняет выделение кучи для объекта Async. - person nadavwr; 09.06.2013
comment
Я бы сказал, что синхронные методы будут иметь другую сигнатуру, поскольку они будут возвращать не Future[A], а просто A, поэтому компилятор может вам это сказать. Вы должны просто разграничить области, в которых вам нужен асинхронный вызов, и импортировать туда неявное преобразование. Что касается предотвращения выделения кучи, я не могу поспорить на это... - person pagoda_5b; 09.06.2013
comment
Увы, Scala не поддерживает перегрузку методов, основанную исключительно на возвращаемых значениях. Тем не менее, предложенное вами неявное преобразование может быть полезным во многих случаях. Что касается распределения кучи - как только JIT определяет, что объект «Async» никогда не покидает кадр стека (чего не должно быть), тогда JIT позаботится о том, чтобы он выделялся только для стека, а не для кучи. - person nadavwr; 09.06.2013
comment
Спасибо за объяснение - person pagoda_5b; 10.06.2013