Flattening the Loan Pattern

The loan pattern is a common pattern for working with resources that should be closed or otherwise managed after use. It removes responsibility from the developer to manage a resource properly. When loaning several objects, one often ends up with nested functions. Here is a way to flatten that to make things clearer.

/**
 * Functions that do the flattening
 */
object Loans {
 
  def loan[T1, T2](f: (T1, T2) ⇒ Unit)(implicit f1: (T1 ⇒ Unit) ⇒ Unit, f2: (T2 ⇒ Unit) ⇒ Unit)
  	= f1 { a ⇒ f2 { b ⇒ f(a, b) } }
 
  def loan[T1, T2, T3](f: (T1, T2, T3) ⇒ Unit)(implicit f1: (T1 ⇒ Unit) ⇒ Unit, f2: (T2 ⇒ Unit) ⇒ Unit, f3: (T3 ⇒ Unit) ⇒ Unit) 
  	= f1 { a ⇒ f2 { b ⇒ f3 { c ⇒ f(a, b, c) } } }
 
}
 
object LoanExample extends App {
 
  import Loans._
 
  // create some fake types for the purpose of illustration
  type Connection = Unit
  type Session = Int
  type User = String
 
  // implicits calling functions with loans
  implicit def withDatabase(f: Connection ⇒ Unit) = f(1)
  implicit def withSession(f: Session ⇒ Unit) = f(1)
  implicit def withUser(f: User ⇒ Unit) = f("Bob")
 
  // conventional loan pattern results in deep nesting
  withDatabase { connection ⇒
    withSession { session ⇒
      withUser { user ⇒
        println("Hi from the depths.")
      }
    }
  }
 
  // flattened
  loan { (c: Connection, s: Session, u: User) ⇒ 
    println("hi from flatland") 
  }
 
}

The nice thing about this is that the implicit argument lists in Loans enable you to call a loan method without passing those functions in explicitly. A problem will be if you need to loan two or more of the same type which is not likely since one is usually borrowing unique resources.

Comments are closed.