Jeroen Kransen Jeroen Kransen - 1 month ago 10
Scala Question

How to make calling code agnostic of implicit parameter passed under the hood?

I want to be able to surround a block of code with a transaction. The calling code should be as simple as this:

transactional {
save("something")
}


I thought to make the
transactional
function like this:

def transactional(block: => Unit): Unit = {
implicit val conn: Connection = ???

conn.begin()
try {
block
conn.commit()
} catch {
case ex: Exception =>
conn.rollback()
throw ex
} finally {
conn.close()
}
}


Now the
save
method will need to do something with the Connection, but I want to make the calling code agnostic of that (see above). I naively implemented it like this:

def save(operation: String)(implicit conn: Connection): Unit = {
println(s"saving $operation using $conn")
}


Of course I get a compilation error, that the Connection can not be found. What part am I missing to wire the Connection from the
transactional
function to the
save
function?

Answer

Change your transactional function something like below (look at the code snippet of transactional). The problem here is connection is available with transactional but it has to reach save function implicitly. So once you get the connection object hand it to the a function which runs inside the transaction and then this code (f) can get access to the connection. Once f gets access to the connection we can make it implicit using the implicit keyword. Now functions like save which take connection implicitly can seamlessly be called inside the transactional.

Important changes to transactional

1) Instead of passing the code block (block: => Unit) pass f (f: Connection => Unit)

2) Inside transactional apply f to connection object and give f access to the connection object.

def transactional(f: Connection => Unit): Unit = {
  val conn = getConnectionFromDatabase()
  conn.begin()
  try {
    f(conn)
    conn.commit()
  } catch {
    case ex: Exception =>
      conn.rollback()
      throw ex
  } finally {
    conn.close()
  }
}

Now you can use it like this

transactional { implicit conn =>
  save("something")
}

if you save function is like this

def save(str: String): Connection => Unit = ???

Then you can go without connection

transactional(save("foo"))