Analytics

Wednesday, July 21, 2010

Software Principles are like some Life Principles

Software principles are useful tools for design and implementation and they help us produce quality products. However, software principles can be compromised at times. They don't always have to be followed as there are exceptions to the rule. In some ways, they are similar to some life principles, and this blog explores that idea.

Software and Life

In life we have ethics and morals that we live by. Ethics and morals manifest themselves as life principles. They give us a framework to become better people, respect one another, and ultimately improve our quality of life.

In the software industry we have software design principles.  They are rules we operate under in order to make the products we develop elegant, easy to understand, and maintainable.  Software products run our economy or make our day to day lives easier, and software principles play a large role in allowing that to happen.

However, software design principles are not meant to be dogmatic. They are not meant to be strictly adhered to.  The use of software principles should be evaluated within the prism of trade offs.  Software principles are essentially rules of thumbs and can be broken if it's the most pragmatic thing to do.

Software principles are like some life principles . . . but unlike others.  To illustrate my point, let's consider some life principles that can be considered absolute, i.e, should never be broken no matter what the circumstances.

Don't Cheat, and Be Nice

Take the rule "Don't cheat." Under no circumstances would I teach my son that it is permissible to cheat.  It's not OK to cheat on a test at school. It's not OK to cheat on your taxes, and its not OK to cheat on a board game at home.  No matter what the context, big or small, cheating is not beneficial. It only hurts others and ultimately your self.  Software principles aren't like the cheating principle. 

Another example would be "Never treat a person as a means to an end." It is unethical to use a person strictly as a means to an end with disregard to their humanity.  People should be treated as human beings, not as objects. Under no circumstances, would I teach my child to "use" someone just for the sake of personal gain and devoid of respect.  Software principles aren't like ethical principles . . . we can break them if needed.

So what are software principles like then, and what software principles am I talking about?  Most life principles we live by are general rules of thumb, they are not absolutes. Software principles are like that. Here are a few examples of what I mean:

DRY

We live by the principle of "Always tell the truth", but this rule doesn't always apply.  Take for example white lies. If your wife asks you, "Do I look fat in this dress?", you would be asinine to say yes.  Most us would say "No honey, you look great!" Even though your beautiful wife may be slightly overweight (no big deal in my eyes, personally.) 

In software, we have the DRY principle: Don't Repeat Yourself. This should be something you mostly do and can greatly contribute to clean code.  But would you really want to create a full fledged Template or Strategy Method Pattern to save 1 or 2 lines of code?  Sometimes violating the DRY principle can avoid excessive pattern usage, which can cripple a project and make code unintelligible. Evaluate the trade offs for DRY and make the best decision.

Law Of Demeter

How about the life principle of "Always eat healthy"?  Yes, we should eat healthy and watch our diet, in general, so we can live a quality life. But we are allowed to break the rule on holidays and eat the fried turkey and pecan pie. It is permissible to go out with the guys and down some beers and wings once in a while. It's not going to kill you.  If eating unhealthy is the exception, it is fine.

The Law of Demeter is a software principle that enables loose coupling and limits the knowledge of one component to another.  Following this rule keeps your code understandable and limits dependencies. But even though its called a "law", it should be viewed more as a guideline.  If you are dealing with an anemic object, and just need to get some data, it is permissible to for a client to dig through objects to get what it needs.  The alternative would be to blow up the API with several needless methods, which is a documented disadvantage of Law of Demeter.

Conclusion

When designing software, we should understand that software principles should be followed in order to produce quality code. However, use them in a pragmatic manner and don't pursue a software principle so hard that it makes your life and code, miserable.  Evaluate your design in terms of trade offs. After all, we certainly have life principles that we don't always follow, and software principles are the same way. Do what's best for value added effort and leave the dogma behind.

Monday, July 5, 2010

Apply Groovy Curried Closures and Composition to Business Rules

Groovy offers new programming idioms
Groovy is a language that operates on the Java platform and is completely compatible with Java.  Many of its features are easily grasped by Java programmers attempting to learn the language. For example, Groovy automatically provides getters and setters for attributes on objects, treats numbers and strings as first class objects, and offers a myriad of programming shortcuts (like << to add to a list or [:] to create a map).

Closures are easy to understand

However, Groovy also contains programming idioms that are not customary in the Java world.  These concepts are borrowed from other languages and empower Groovy to be very effective and dynamic in solving business problems.  The concept of closures is one example.  Closures are a prime feature in Groovy, but do not exist in Java (although Java 7 is scheduled to contain closures). A closure is a defined block of code that can be passed around and executed at a later point.  They are commonly used when iterating over collections, and they eliminate much of the code noise that comes with Java iteration.  With some effort, closures in their most common application are fairly simple to grasp.

More complex Groovy idioms

Some concepts are more foreign to the conventional Java programmer. Curried closures are an extension to the concept of closure.  Currying a closure takes a little more effort to understand and is certainly not a concept common to Java.  Groovy also allows for composition of closures, where closures are combined together so that closures operate on other closure results.  These techniques are part of the functional programming paradigm which emphasizes the application of functions, rather than methods conducting state changes on data.  Haskell, Schema, and Erlang are functional programming languages.  Through an example, this article will demonstrate how curried closures and composition can be applied to solving business world problems.

Curry Definition

First let's define what a curried function is. The term curry comes from the mathematician Haskell Curry who developed the concept of partial functions.  In functional programming, currying a function means transforming a function into another function by fixing some of its input arguments.  Currying is good for mathematical business problems where you need to break the problem down into pieces.  Essentially, you have a template mathematical formula and you can assign fixed values to a portion of the formula to create new version of the template. A simple math example follows:

f(x, y) = x / y   ---- template
f(x, 2) = x / 2   ---- curry with fixed value
g(x) = f(x, 2)   ---- this is the new curried function
g(10) = 10 / 2 = 5

Function Composition Definition

Function composition is the application of one function to the result of the other.  Using composition, two or more functions can be combined to produce a more elaborate one.  A simple examples follows:

f(x) = x * 2
g(y) = y / 100
f(g(y)) = (y / 100 ) * 2

Investment Business Problem #1

Let's define a business problem that would be interesting to solve with Groovy using curried closures and function composition.  One we clearly see the problem definition, we can apply the programming concepts and illustrate the power of its usage.  

Bob wants to invest some money for retirement. He is 55 years old and will retire at 65.  He has $5000 total to play with.  He has found an investment vehicle that will deliver 8% interest every year for 10 years (compounded once a year). He is not sure whether to invest all of the $5k or only a portion of it.  He wants to see the difference. 

Used a Curried Closure 

Recall that in Groovy, we can create closures which are basically passable functions.  Here we can create a closure that encapsulates the investment formula. 

def interestFormula = { r, y, p -> p * Math.pow(1+(r), y) }

In order to reduce the formula to a specific context, we can replace some of the variables with fixed values.  Currying will carry out this task. Then we can reuse the closure to test out various principle investments.  


package curry.blog

def interestRate = 0.08
def years = 10
def principle1 = 3000
def principle2 = 5000

// create current investment from interest formula with set rates and 
// see what differences in savings will pay off
// years down the road
def interestFormula = { r, y, p -> p * Math.pow(1+(r), y) }
def invest = interestFormula.curry(interestRate, years)

println "invest ${principle1} and return: " + invest(principle1) 
println "invest ${principle2} and return: " + invest(principle2)

From the example above, the output is:

invest 3000 and return: 6476.7749918183645
invest 5000 and return: 10794.62498636394

Clearly, Bob can see that $5k investment returns almost $4k more than a $3k investment. If he can afford to put all $5k for retirement, it would pay off well. 

Investment Business Problem #2

Now we are ten years down the line, and Bob is ready to liquidate his investment.  However, he encounters a money savy friend who says he found a firm that will rollover his retirement fund and actually pay him 10% to manage his money. In order to rollover his retirement account, he must pay the existing firm $100.  Thus, Bob is considering liquidating his account and paying the government 30% tax to keep his money, or continue to invest by rolling over and gaining 10%, essentially deferring his taxes until later.  He wants to evaluate his initial $5k investment against the different paths he can take.

Used a Curried Closure and Composition 

We can create a closure to represent money after tax is applied.  We can then curry it to be specific to the 30% tax bracket.

def taxFormula = {  t, p ->; p * (1 - t) }
def applyTax = taxFormula.curry(taxRate)

For a rollover, we can create a simple closure as well. No currying necessary here as there is only on variable.

def doRollover = { z -> (z - rollOverFee) * (1 + rollOverMatching) }

Bob wants to compare the situation where he liquidates against when he reinvests.  A composition closure addresses this nicely.  We can create a composition template where you can apply different actions after the investment is liquidated.  Curry the composition with the investment closure, then leave the other variables open for the principle and the action.  Essentially the resulting closure does the investment result for you, then applies the various actions to the result.

def composition = {g, x, f -> f(g(x)) }
def considerPostInvestmentAction = composition.curry(invest)

See the entire Groovy script.  considerPostInvestmentAction calculates the investment, then tells you what the result is after you liquidate it and pay taxes, or roll it over.  It can accept a closure and an initial principle. The closure passed in represents the action taken on the liquid value, either tax applied or a rollover.  Bob can compare the differences.


package curry.blog

def interestRate = 0.08
def years = 10
def taxRate = 0.30
def principle2 = 5000
def rollOverFee = 100
def rollOverMatching = 0.10

def interestFormula = { r, y, p -> p * Math.pow(1+(r), y) }
def invest = interestFormula.curry(interestRate, years)

// tax formula which tells you how much you will have after taxes
def taxFormula = {  t, p -> p * (1 - t) }
// curry it with the tax Rate
def applyTax = taxFormula.curry(taxRate)
// formula for rollover; the amount you will have after rollover
def doRollover = { z -> (z - rollOverFee) * (1 + rollOverMatching) } 
// template for functional composition
def composition = {g, x, f -> f(g(x)) }

// use composition and currying to allow you apply a function on top of your 
// investment after you sell it. Thus you can decide 
// whether to pay taxes and keep the income, or roll over into another account
def considerPostInvestmentAction = composition.curry(invest)

println "initial investment is ${principle2}. After taxes it is: " + considerPostInvestmentAction(principle2, applyTax)
println "initial investment is ${principle2}. After rollover it is: " + considerPostInvestmentAction(principle2, doRollover)
The print lines reveal:


initial investment is 5000. After taxes it is: 7556.237490454758
initial investment is 5000. After rollover it is: 11764.087485000335 

Should Bob have the ability refrain from liquidating his investment, he can gain big. His $5k turns into ~$12k.  And he can hope that later down the line, he will be in a lower tax bracket and he can keep more of his money.  If he withdraws now, he is still in the 30% tax bracket and his investment only comes back with ~$7500. 

Conclusion

This business problem can certainly be solved with conventional Java paradigms. However, Groovy is rich in features as well as programming idioms.  Groovy gives the developer a chance to solve programs in a concise, elegant fashion.  If a business problem has functional and mathematical qualities, consider functional programming to solve the issue.