scalaimplicitconvention

Scala: Why use implicit on function argument?


I have a following function:

def getIntValue(x: Int)(implicit y: Int ) : Int = {x + y}

I see above declaration everywhere. I understand what above function is doing. It is a currying function which takes two arguments. If you omit the second argument, it will invoke implicit definition which returns int instead. So I think it is something very similar to defining a default value for the argument.

implicit val temp = 3

scala> getIntValue(3)
res8: Int = 6

I was wondering what are the benefits of above declaration?


Solution

  • Here's my "pragmatic" answer: you typically use currying as more of a "convention" than anything else meaningful. It comes in really handy when your last parameter happens to be a "call by name" parameter (for example: : => Boolean):

    def transaction(conn: Connection)(codeToExecuteInTransaction : => Boolean) = {
    
       conn.startTransaction  // start transaction
    
       val booleanResult = codeToExecuteInTransaction //invoke the code block they passed in
    
      //deal with errors and rollback if necessary, or commit
      //return connection to connection pool
    }
    

    What this is saying is "I have a function called transaction, its first parameter is a Connection and its second parameter will be a code-block".

    This allows us to use this method like so (using the "I can use curly brace instead of parenthesis rule"):

    transaction(myConn) {
    
       //code to execute in a transaction
      //the code block's last executable statement must be a Boolean as per the second
      //parameter of the transaction method
    
    }
    

    If you didn't curry that transaction method, it would look pretty unnatural doing this:

    transaction(myConn, {
    
       //code block
    
    })
    

    How about implicit? Yes it can seem like a very ambiguous construct, but you get used to it after a while, and the nice thing about implicit functions is they have scoping rules. So this means for production, you might define an implicit function for getting that database connection from the PROD database, but in your integration test you'll define an implicit function that will superscede the PROD version, and it will be used to get a connection from a DEV database instead for use in your test.

    As an example, how about we add an implicit parameter to the transaction method?

    def transaction(implicit conn: Connection)(codeToExecuteInTransaction : => Boolean) = {
    
    }
    

    Now, assuming I have an implicit function somewhere in my code base that returns a Connection, like so:

    def implicit getConnectionFromPool() : Connection = { ...}
    

    I can execute the transaction method like so:

    transaction {
       //code to execute in transaction
    }
    

    and Scala will translate that to:

    transaction(getConnectionFromPool) {
      //code to execute in transaction
    }
    

    In summary, Implicits are a pretty nice way to not have to make the developer provide a value for a required parameter when that parameter is 99% of the time going to be the same everywhere you use the function. In that 1% of the time you need a different Connection, you can provide your own connection by passing in a value instead of letting Scala figure out which implicit function provides the value.