Chris Beach Chris Beach - 4 months ago 24
Scala Question

Binding/Injecting a Scala function (not whole class) using Guice?

I'd like to inject the

method of my
object into the constructor of my
object. I want to avoid injecting the whole class.

// Has def byId(id: UserId): Option[User]

// Something like this
bindMethod(classOf[UserDao], _.byId)

// Constructor takes a (UserId) => Option[User] function

I'm using Guice with the Play Framework. Any suggestions appreciated


You're right to ask this as it can greatly simplify testing. The trick is to use the @Provides annotation:


class MyController @Inject() (byId: (UserId) => Option[User]) extends Controller { ... }

def userDao(aDependency: Any): UserDao = // return UserDao, note aDependency will be injected

def byId: (UserId) => Option[User] = userDao.byId // note: this method will call the other @Provides method 'userDao'


Ultimately byId: (UserId) => Option[User] translates to scala.Function1[UserId, Option[User]] however you can declare function dependencies using the syntactic sugar:

class MyController @Inject() (_addTwo: (Int, Int) => Int) extends Controller {

  def addTwo(a: Int, b: Int) = Action {
    Ok(_addTwo(a, b).toString) // call the injected function 


Then in your Module.scala create a method which returns the function:

def addTwo: (Int, Int) => Int = (a, b) => a + b

You can do all the usual Scala stuff in the @Provides method, e.g. returning a partially applied function:

def addTwo: (Int, Int) => Int = addThree(0, _:Int, _:Int)

def addThree(a: Int, b: Int, c: Int): Int = a + b + c

To avoid conflicts you can also use the @Named annotation:

class MyController @Inject() (@Named("addTwo") _addTwo: (Int, Int) => Int, 
                              @Named("subtractTwo") _subTwo: (Int, Int) => Int) 
                              extends Controller { ... }

def addTwo: (Int, Int) => Int = (a, b) => a + b

def subTwo: (Int, Int) => Int = (a, b) => a - b