Software has two ingredients: opinions and logic (=programming). The second ingredient is rare and is typically replaced by the first.
I blog about code correctness, maintainability, testability, and functional programming.
This blog does not represent views or opinions of my employer.

Friday, March 30, 2012

Imperative curlies 4: shorten the distance.

Continuation of my previous bashing of curlies.
My previous posts can be summarized by stating the following rule: Look at curly brackets surrounding imperative code and redesign your code so they disappear. There are obviously various tricks for disappearing the curlies and I will write more as I keep discovering them.

Here is a somewhat relaxed version of this rule:  If you cannot get rid of curlies, redesign your code so the distance between curlies is as short as possible. (Redesign your code so the imperative part between curly brackets does as little as possible). Let me clarify my postion: It is not about squeezing as much as possible into one line, the goal is to simply isolate reusable code, create reusable utilities, and keep the client imperative code to minimum. If you cannot be declarative, at least make sure that the imperative code is reduced to minimum. The distance between curlies is simply a guide to measure the progress.

We have seen in the previous post that SCALA let’s you treat one-liner functions in declarative fashion by removing curly brackets. This allows you to define functions in a math formula-like style. Examples without type inference and without much syntactic sugar of using ‘_’:
1:  def doubleIt(d: Double): Double = 2*d  
2:  def halfIt(d: Double): Double = 0.5 *d  
3:  def sortOfIdentity(d: Double): Double = halfIt _ andThen doubleIt 
//Note current SCALA compiler needs ugly _ with andThen  
Language like Groovy will not let you be as elegant. Here are the Groovy equivalents (also without using much syntax sugar):
1:  def composition = {f, g, x -> return f(g(x))}//composition helper  
2:  Closure doubleIt = {Double d -> 2* d}  
3:  Closure half = {Double d -> 0.5 * d}  
4:  Closure sortOfIdentity = composition.curry(halfIt, doubleIt)     
(I am repeating myself here , but note that the term curry in not used correctly in Groovy.)
The point is that the above code snapshots are equivalent. What is important is that the benefits on testablility and maintainability are the same.

Example of Java code from an open source ArrayUtil found here:
1:  public class ArrayUtils {  
2:  ...  
3:   public static long sum(  
4:     int[] source  
5:    )  
6:    {  
7:     int iReturn = 0;  
9:     if ((source != null) && (source.length > 0))  
10:     {    
11:       int iIndex;  
13:       for (iIndex = 0; iIndex < source.length; iIndex++)  
14:       {  
15:        iReturn += source[iIndex];  
16:       }  
17:     }  
18:     return iReturn;  
19:    }  
20:  }       
Big distance between curlies in the implementation of the sum method. How reusable is this method code? What if we wanted to create ArrayUtil.multiply(int[] source) or ArrayUtil.max(int[] source), or ArrayUtil.min(…)?

So how can I shorten the distance between the curlies? Let us move to Groovy to get some ideas. The fact that we have a for loop suggests that we can try use one of the reusable pieces of logic available to us as a replacement for explicit for loops (see my previous post).

(Note: Functional programming uses the term folding, for some reason Groovy calls the equivalent method inject.)   Compare reusability of the above Java code with the following code in Groovy:
1:  def myInts = [1,2,3,4,5];  
3:  def multipliedInts = myInts.inject(1) {acc, val-> acc * val}  
4:  def addedInts = myInts.inject(0) {acc, val -> acc + val}  
5:  def minFromInts = myInts.inject(Integer.MAX_VALUE) {acc, val ->
6.                                    Math.min(acc, val) }  
7:  def maxFromInts = myInts.inject(Integer.MIN_VALUE) {acc, val ->
8.                                    Math.max(acc, val) }  
(Side Note: There is a problem here: what is the value of minFromInts if myInts was an empty array?  Functional programming introduces concepts of monads, SCALA has Option Some and Option None concepts, but these are not in the scope for this post.)

Note that the distance between curlies in the above Groovy example is as close as one can get to defining functions using plain math like formulas. However, personally I tend to prefer a more declarative style shown below:
>1:  class MathFormulas {  
2:   def add = {acc, val -> acc + val}  
3:   def multiply = {acc, val -> acc + val}  
4:  }   
6:  def multipliedInts = myInts.inject(1, MathFormulas.multiply)  
7:  def addedInts = myInts.inject(0, MathFormulas.add)   
8:  def minFromInts = myInts.inject(Integer.MAX_VALUE, Math.&min)  
9:  def maxFromInts = myInts.inject(Integer.MIN_VALUE, Math.&max)  
You can find specialized methods in Groovy for all of these tasks, however, the point here is the code reuse aspect. The Groovy's inject method is clearly reusable, while the for loop in Java is clearly not. How would you change the Java code above following these Groovy examples?

Is this Groovy magic? Not really, sure having closures helps, in Java we can work with reusable interfaces (similar to some that can be found in Functional Java ). We can write simple Collection Utility with fold method (it will be more verbose) :
1:  //direct from Functional Java project ...  
2:  public interface F2<A, B, R> {  
3:    R f(A a, B b);  
4:  }  
6:  //our own utility to see what needs to be done ...  
7:  public class MyCollectionUtils {  
8:   static <T, L> T fold(Iterable<L> list, 
9:                       T iniValue, 
10:                      F2<? super L, ? super T, ? extends T> fun){  
11:    T res = iniValue;   
12:    for(L l: list){  
13:       res = fun.f(l, res);  
14:    }   
15:    return res;   
16:   }  
17:  }  
19:  F2<Integer, Long, Long> multiply = new F2<Integer, Long, Long>() {  
20:   public Long f(Integer a, Long b) {  
21:     return b * a;  
22:   }  
23:  };  
15:  List<Integer> list = ...  
26:  long multipliedInts = MyCollectionUtils.
27.                <Long, Integer>fold(list, 1l, multiply);    
The difference is largely in how programmers think. New book title idea: Unlearn Java in 24 days?

Let me sum up what happened to Java code: we went from no code reuse to high code reuse. The measure of distance between culries is about 10 lines for the original code and 1 line for a function interface F2 we would need to implement in the final code:  
return a + b;

I hope to continue the curlies bashing soon.

1 comment: