Monday, February 23, 2015

Partially applied functions and implicit parameters

In trying to test an akka Actor talking over TCP (a whole difficult subject of its own), I wanted to inject a factory method for the TCP actor ref into my Actor. Specifically, I wanted to inject IO.apply. The signature looks like:

// Used like "IO(Tcp)", where Tcp is the object identifying the TCP IO extension.
def apply[T <: Extension](key: ExtensionId[T])(implicit system: ActorSystem): ActorRef

Ignoring the awkward type parameter, this looks like a method with two parameter lists - one normal, and one implicit. My thought was that we could just bind the explicit list, and let the implicit list get bound later. Note that I couldn't figure out how to create a partially-applied function that was 0-ary (that is, all parameters were bound), so I had to have some weird typing:

import akka.actor.{ Actor, ActorRef, ActorSystem, Props }
import akka.io.{ IO, Tcp, TcpExt}
// Note that I hard-coded the type to Tcp for readability.
class MyActor(tcpFactory: Tcp.type => ActorRef) extends Actor {
  // To use:
  tcpFactory(Tcp) ! Tcp.Connect(hostname)
}
object MyActor {
  // Factory method for the class, used outside of unit tests.
  // WARNING: Can you spot the error here?
  def props()(implicit actorSystem: ActorSystem): Props = {
    // Constrain the type on the apply method, and partially bind it.
    Props(new MyActor(IO.apply[TcpExt] _))
  }
}

After this, I compiled & tested the code - it worked! But I was left with an unsettled feeling about the implicit, and thinking for a moment, it struck me: This potentially runs the factory method (IO.apply) in a different ActorSystem than the main actor itself! The reason being, of course, that the implicit is bound up in the partial method when you first call props(), not when it's later called within the Actor. This should be obvious by the partial's signature - Type.type => ActorRef doesn't have an ActorSystem anywhere in it!

The solution would be to bind both the implicit and explicit list to the partial - but I couldn't find any documentation on how to bind an implicit list into a partially-applied function, or even to declare it as such. From what I can tell, this is impossible. You can curry functions with multiple regular parameter lists, but you're out of luck for implicits. However, you can bind a partial function with a single argument list with the implicits tacked on:

class MyActor(tcpFactory: (Tcp.type, ActorSystem) => ActorRef) extends Actor {
  // Use 'implicitly' to get the value from the implicit scope.
  tcpFactory(Tcp, implicitly[ActorSystem]) ! Tcp.Connect(hostname)
}
object MyActor {
  // No more implicit parameters on the factory method!
  def props: Props = {
    // Looks like currying, confusingly.
    Props(new MyActor(_: Tcp.type)(_: ActorSystem))
  }
}

Cleaning up a bit more:

// Partially bind the Tcp parameter now.
class MyActor(tcpFactory: ActorSystem => ActorRef) extends Actor {
  tcpFactory(implicitly[ActorSystem]) ! Tcp.Connect(hostname)
}
object MyActor {
  def props: Props = Props(new MyActor(Tcp)(_: ActorSystem))
}