I recently gave a live coding session, where I demonstrated functional concepts and how powerful they are for keeping concerns separated.
The exercise consisted of how to separate what you do with a resource, from how you handle the lifecycle of that resource.
The problem can look something like this:
def writeToFile(file: File) {
val writer = new PrintWriter(file)
try {
writer.println("Hello Kitty")
writer.println(new Date)
} finally {
writer.close
}
}
This function is totally locked down. It isn't even easily testable because "now" is written to the file, even though we have no way to influence what is written we can't test "now" since it is an ever moving concept.
The solution is simple, put the handling of the PrintWriter's lifecycle inside the function and put what to write to it in the call to the function.
The function now looks like this:
def writeTo(file: File)(op: PrintWriter => Unit) {
val writer = new PrintWriter(file)
try {
op(writer)
} finally {
writer.close
}
}
And calling it looks like this:
val now = new Date
writeTo(file)(
writer => {
writer.println("Hello Kitty")
writer.println(now)
})
Here we can test "now" since it is declared outside the function call. The function turns out to be fully reusable and we can write whatever and however we want to the file.
Furthermore, when writing the tests for this function, the same exact pattern popped up two more times - separating the lifecycle of a resource - from the use of that resource.
Once when validating the written content in the file, and once for handling the test file itself.
So, separating concerns turns out to be very easy in Scala.
-------------------------------------
Reflecting on this I came to an realization:
When using a language that really help you separate concerns, you are much less dependent on the external framework making things pretty for you.
-> An api that
"do stuff" can be made
much more focused, since it's so easy to make the "use of it" pretty.
But there is another side to this.
When writing my tests I use Specs, this because I like to formulate my thinking while testing in a BDD way.
-> There is another way to write frameworks, the DSL way, who's main purpose is to let you formulate your thoughts.
When writing an api it is tempting to always go the "Think" way because it appears to be more profound, but this can make things more complicated than it has to be.
A
Think framework is usually harder to pick up, since the picking up consists of
learning the problem domain described by the DSL.
Picking up a
Do framework mostly consists of finding out
what function to call.
When using a truly full-featured language like Scala it is important to realize that you doesn't have to provide for everything.
-> And a
tight, slick "Do"-api can be
very valuable and is
easier to write.