Moje zdjęcie
Software Craftsman's Blog by Marcin Pieciukiewicz
Java and Scala development

Sunday, July 21, 2013

Metaprogramming in Scala with higher order functions

Scala is very flexible programming language. In this article I would like to propose some usage pattern for higher order functions. This pattern could help us to create a convenient way to handle multiple situations such as:
- monitoring code performance
- exception logging
- executing commands in separate Thread
- transaction handling
- try with resource
- environment changing

Below I'm giving an usage examples and sample implementations for every of this cases.

Higher order function as meta-data
Basically higher order function is a function that takes another function as a parameter. In the following example calculate is a higher order function that takes function sum as parameter :
scala> def sum(a:Int, b:Int) = a + b
scala> def calculate(a:Int, b:Int, operation: (Int, Int) => Int) = operation(a, b)
scala> calculate(5, 3, sum)
res1: Int = 8
We will leverage the fact that we can pass a function that does not take any parameters, and does return nothing. That means that we'll simply pass a code block as a parameter to our function. This way we can do this:
//implementation
def doItTwice(codeBlock: => Unit) {
  codeBlock
  codeBlock
}
//usage with full syntax
doItTwice({
  println("Hello!")
})
and to do this even nicer we can skip parentheses in doItTwice call, so it will look this way:
//proper usage with abbreviated syntax
doItTwice {
  println("Hello!")
}
the result is simple to predict:
Hello!
Hello!
Doesn't it feel like we have extended language we are using? Now it's almost like adding simple metadata to our application.
This feature can be leveraged to improve our programs, but before that we have to use another Scala feature called currying. It allows us to define multiple parameters lists for a function. Thanks to that, we will be able to pass another parameters to our higher function. Will take a closer look at this by introducing new loop implementation.

Simple loop
Let's assume we want to define a simple way to loop our program for given number of times. And we want to do this as simple as possible. In example we would like to use it in this way:
//sample usage
repeat(5) {
  println("Cool loop")
}
Now we should be able to do this easily. This is the solution:
//implementation
def repeat(times: Int)(codeBlock: => Unit) {
  var i = 0
  while (i < times) {
    codeBlock
    i += 1
  }
}
Thanks to the currying we could have passed times parameter and still be able to use abbreviated syntax when passing a code block. This is very simple usage of this pattern, but it uses every feature of Scala we need. Let's go to more serious problem.

Execution time logging
Next case. Let's assume that we have some complex block of code, and we have to monitor it's execution time. So the nice way to do this would be like that:
//sample usage
logExecutionTime("My complex operations") {
  complexOperation1
  complexOperation2
  complexOperation3
}
with the result looking like this:

Execution time of "My complex operations" was 125 mills

To achieve this we can simply define logExecutionTime function this way:
//implementation
def logExecutionTime(name: String)(codeBlock: => Unit) {
  val start = System.currentTimeMillis()
  codeBlock
  val end = System.currentTimeMillis()
  println("Execution time of \""+ name +"\" was "+ (end - start)+" mills")
}
Btw. I have proposed a little more complex solution to measure time execution and described it in Neat and simple way to measure code performance in Scala article.

Exception logging
Now let's think about exceptions. There are many cases when you need just output the information about exception that occurred, but your application or maybe container of the application will handle that exception somewhere else. In the following example logExceptions will catch exception thrown from inner code block, write it out to a console, and than rethrow it, so the program can handle it:
//sample usage
logExceptions {
  operationThatMightCauseException
}
And this is simple implementation of it.
//sample usage
def logExceptions(codeBlock: => Unit) {
  try {
    codeBlock
  } catch {
    case e:Exception => {
      e.printStackTrace()
      throw e
    }
  }
}
I hope that now you've got feeling that this approach can be treated as metaprogramming, in this case logException can be treated as an annotation to a given code block. And, in my humble opinion, this is very nice. So let's go to further possible usages of this pattern.

Execute commands in separate Thread
So maybe we would like to execute some commands separately to our main program thread, ie. to send a message to external server. It have to be executed in separate thread, so it wouldn't block our application because of network delays. So that might be the way we would like to program it:
//possible usage
fireAndForget {
  sendMessageViaHttp
}
In the simplest solution we'll just create a new thread and run a given block of code in that thread:
//implementation
def fireAndForget(codeBlock: => Unit) {
  new Thread(new Runnable {
    def run() {
      codeBlock
    }
  }).start()
}
Transaction handling
Now let's think about a common pattern in enterprise application - a transaction handling in database communication. In simplest situation you will to start a transaction, then execute your database commands and in the end you will commit the transaction or rollback it if something went wrong. We would like to help a programmer to do this multiple of times, so it would be nice if he could simply declare a transaction around the database commands. In the example I've assumed that our database commection is represented by simple trait with three methods to control a transaction:
trait Connection {
  def beginTransaction()
  def commit()
  def rollback()
}
And now the programmer should be able to program like that:
//possible usage
transaction(postgresConnection) {
  insertRecords
  updateOtherRecords
}
The transaction is responsible for creating a transaction, executing commands and commiting or rollbacking transaction (on the exception occurrence). So it's implementation could look like that:
//implementation
def doInTransaction(connection: Connection)(codeBlock: => Unit) {
  connection.beginTransaction()
  try {
    codeBlock
    connection.commit()
  } catch {
    case e:Exception => {
      connection.rollback()
      throw e
    }
  }
}
Try with resource or simple resource handling, ie. file reading
Java 7 introduced try with resources statement that facilitated usage of external resources that have to be handled with care. In example they have to be properly closed to be reused. Before that resources handling was error prone because programmers often forgot to close the resource. So let's look how we could simplify it in Scala with the example of file reading. One of the easiest way to read file could be like that:
//possible usage
readFile("file.txt") {reader =>
  println("First line " + reader.readLine())
  println("Second line " + reader.readLine())
  println("Third line " + reader.readLine())
}
And the fileRead implementation should properly open a file, and at the end close it properly. That could be implemented in this way:
//implementation
def readFile(filename:String)(codeBlock: BufferedReader => Unit) {
  val reader = new BufferedReader(new FileReader((filename)))
  try {
    codeBlock(reader)
  } finally {
     if(reader != null) {
       reader.close()
     }
  }
}
Environment change
For the last example let's assume that we have a parts of application that depend on some specific environment configuration. What if we have to change it temporarily to do something specific, for example for test purposes.
So let's assume that our environment is accessible for us by the singleton object that holds String to String map:
object Environment {
  var environment = Map[String, String]()
}
And for our example let's assume that our environment contains an entry that holds ip address for some name server: "nameServer" -> "192.168.10.210". If we would like to alternate it temporarily to redirect request to localhost we would like to do something like that:
//possible usage
println(Environment.environment("nameServer"))

changeEnvironment("nameServer" -> "127.0.0.1") {
  println(Environment.environment("nameServer"))
}

println(Environment.environment("nameServer"))
and that should give us that output:
192.168.10.210
127.0.0.1
192.168.10.210
To achive this, one possible implementation of changeEnvironment could look like this:
//implementation
  def changeEnvironment(change: (String, String))(codeBlock: => Unit) {
    val oldEnvironment = Environment.environment
    Environment.environment = Environment.environment + change
    codeBlock
    Environment.environment = oldEnvironment
  }
The last example was inspired by Tomasz Nurkiewicz in his blog article Fake system clock pattern in Scala with implicit parameters. He proposed a solution for switching a Clock object, that is used within application, for the purpose of test that might require testing against given date. In the comment to that article I've proposed a different solution, than given by Tomasz, based on environment changing described above.

Conclusion
I hope I've given you an idea that Scala can be used for metaprogramming with very simple constructs. When we'll use it properly, it can easily separate a business logic of an application from technical details of the implementation and this will greatly improve the way the source code can be read and managed.

Previous article: Neat and simple way to measure code performance in Scala

1 comment:

  1. Use our slot search software to find out|to search out} your favorite slot machine games at Evangeline Downs Racetrack & Casino in south central Louisiana. Wink Slots supply gamers numerous themed games that each one|that each one} have a definite theme tune and brightly colored graphics, making the games a lot more attractive than an average casino game like roulette. This tells gamers on common, how much cash they'll count on to lose from each $100 spent wagering. Despite this determine proving to gamers that they will lose greater than they win on common, folks proceed 1xbet taking part in}. The reasons behind the success of slot machines lies in our brains, deep within our psyche which we are going to discover on this article.

    ReplyDelete