16

Can I turn a method which takes an implicit parameter into a function?

trait Tx

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

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

I am trying to achieve the following, preferably if I can somehow make it work with the plain call 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
}

I found this question, however it does not deal with the lifting from methods to functions as far as I can see.

0

2 Answers 2

9

Implicits only work for methods. But you have to pass a function to withSelection. You can get around by wrapping the method in a function:

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

Its impossible to pass deleteObjects directly because foo _ does not work for a foo with an implicit parameter list defined.

Sign up to request clarification or add additional context in comments.

Comments

4

To the best of my knowledge, implicit resolution must take place at usage site, and cannot be curried away. My own moment of disappointment was when I was trying to work around ´ExecutionContext´ proliferation in my code.

One compromise I've been considering was:

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

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

The ´implicit´ only holds within the function -- we have to compromise on invocation:

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

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

Another compromise -- the one I chose and lived to regret:

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

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

This changes the usage from the naive (but desired):

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

To the following:

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

Depending on your context, this could be bearable. You could add a ´def transactional´ where I used ´def async´.

I do regret this however, as it complicates inheritance, and incurs some allocation overhead (though that should be JITed away).

Bottom line is that you'll have to come up with a more explicit piecemeal method of invoking your function -- one that is less elegant than currying alone.

4 Comments

About your second case (i.e. FileCounter), couldn't you define an implicit conversion implicit def counterToAsync(c: FileCounter): AsyncFileCounter = c.async so you can once again counter.countFiles("/") ?
Nice! In my case the implicit conversion will be defeated by synchronous methods with similar signatures that exist on the containing object. However, it's definitely a good boilerplate workaround! I really need to verify that escape analysis really does eliminate the heap allocation for the Async object, though.
I'd say that synchronous methods would have a different signature, since they won't return a Future[A] but simply an A, so the compiler can tell you that. You should simply delimit scopes where you need the async call and import the implicit conversion there. As for avoiding heap allocation I can't bet on that...
Alas, Scala does not support method overloading based solely on return values. Nonetheless, the implicit conversion you suggested can be useful in many cases. Regarding heap allocation -- once JIT determines that the 'Async' object never leaves the stack frame (which it shouldn't), then JIT will make sure it will allocate to stack only instead of heap.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.