Friday, January 20, 2012

Translating If-Then-Else Control Flow Idiom to F#

I was reading through Juval Löwy's Programming WCF Services book and wondering if I should do a series of WCF blog posts in F# based on Löwy's book when I ran into a common construct found in C# programs. That construct looks something like the following C# code:

    public static void MyMethod(String oldstuff, String newstuff, bool flag)
    {
        if (oldstuff == null)
         throw new Exception("oldstuff is null!");

        if (newstuff == null)  {
            DoSomething("Default");
            return;
        }
        if (flag == false)  {
            DoSomething(oldstuff);
            return;
        }
        DoSomething(newstuff);
    }

This is a construct that I oftened have used in the past and have never thought about it much. But when you translate the above code directly into F#, it becomes a lot more verbose because F# requires you to implement the then clause. A direct translation to F# as follows:

let mymethod oldstuff newstuff flag =
    if oldstuff = null then 
        raise (new Exception("oldstuff is null!"))
    else
        if newstuff = null then
            DoSomething("Default")
        else
            if flag = false then
                DoSomething(oldstuff)
            else
                DoSomething(newstuff)

If I had a lot of these if-then-else statements in my C# method, then my F# version would disappear off the screen to the right if I tried to implement it by direct translation. I thought about how I could implement this in F# and came up with this following possibility:

let revised_mymethod oldstuff newstuff flag =
    let (_,action) =
        [(oldstuff=null,  lazy (raise (new Exception("oldstuff is null!"))));
         (newstuff=null,  lazy (DoSomething("Default")));
         (flag=false,     lazy (DoSomething(oldstuff)));
         (flag=true,      lazy (DoSomething(newstuff)))]
        |> List.filter fst
        |> List.head
    action.Force()

Rewriting the C# code in this fashion makes me think of rules engines and after refactoring out some common code, I could rewrite the above F# code as follows:

let followrules (xs:(bool*Lazy<unit>) list) =
    (xs |> List.filter fst |> List.head |> snd).Force()

let revised_mymethod2 oldstuff newstuff flag =
    [(oldstuff=null , lazy (raise (new Exception("oldstuff is null!"))));
     (newstuff=null,  lazy (DoSomething("Default")));
     (flag=false,     lazy (DoSomething(oldstuff)));
     (flag=true,      lazy (DoSomething(newstuff)))]
    |> followrules

With this new construct, I can easily re-arrange the order of evaluation, add or remove new conditions. This new construct just seems to have more advantages than the old if-then-else construct in F#.

Wednesday, January 11, 2012

Testing Coherence with Clojure

A developer came to me the other day asking for help in diagnosing some issues with their application and the interaction with Oracle's Coherence product. I wanted to write some testing harness to quickly test some Coherence configuration and gave some thought about how I would go and try to replicate the issues that the application had. I wanted a REPL environment so that I can interactive manipulate the Coherence API and dump outputs on demand. I decided to use Clojure to experiment with Coherence, although I could have used JRuby, Jython, Groovy or Scala. From purely a familiarity perspective, I would rank my usage of these listed languages in the order of Ruby first, Python second, Groovy third, Clojure fourth and Scala last. But for some unknown, deep-seated and probably emotional reasons, I like Clojure more and relish the opportunity to use it. One of the first thing I tried with Clojure and Coherence is to perform a timing test on adding data to a 2 node distributed cache in serial vs concurrent mode. Here's the example Clojure script:

(import '(org.apache.commons.lang3 RandomStringUtils) 
        '(java.math BigInteger)
        '(java.util Random Date HashMap)
        '(com.tangosol.net NamedCache CacheFactory CacheService Cluster))

(CacheFactory/ensureCluster)
(def cache (CacheFactory/getCache "sandbox"))

(defn random-text [] (RandomStringUtils/randomAlphanumeric 1048576))
(defn random-key [] (RandomStringUtils/randomAlphanumeric 12))


; Testing serial puts
(new Date)
(dotimes [_ 200] (.put cache (random-key) (random-text)))
(new Date)

; Testing concurrent puts
(def buffer (new HashMap))
(new Date)
(dotimes [_ 200] (.put buffer (random-key) (random-text)))
(.putAll cache buffer)
(new Date)

Running this code showed a 2x gain in speed of data load. On one of the Coherence nodes, I had JVisualVM connected to it and watched the realtime GC behaviors with VisualGC. It has been fascinating to watch the behaviorial differences between serial vs parallel data load and the memory activities of the Coherence node when my Clojure script was idle. I hope to conduct more tests in the future looking at GC behaviors and leverage my Clojure script as load driver in my testing efforts and assist me in GC tuning of Coherence instances.