Monthly Archive for August, 2008

Pattern matching in OCaml (and JavaScript?)

I’ve been trying to learn OCaml lately.
I want to try information visualization for the “dektop” (as opposed to the web), and although I know C++ or Java would be recommended languages for this kind of thing (they have lots of libraries and pretty large communities regarding visualization in general -think about games, OpenGL, etc), I wouldn’t feel very comfortable doing geometric computation in C++ or Java.
Although geometric computation often handles objects, there’s also the “functional side” of it that doesn’t seem to be fulfilled by C++ or Java. Ocaml felt like the right mix in of programming paradigms to do this kind of thing, so I began to read Introduction to Objective Caml by Jason Hickey.

One of the chapters I really enjoyed about this book is Basic pattern maching. I was pretty amazed about the predominance of pattern matching in OCaml. Pattern matching can be applied to (almost) any OCaml primitive type (string, Boolean, char…), it also applies to a variety of syntactic structures (like assignment, function definition, etc) and finally there’s a lot of “syntactic sugar” going on in pattern matching.

The basic syntax for doing pattern matching in Ocaml is:

match expression with
 | pattern1 -> expression1
 | pattern2 -> expression2
 .
 .
 .
  | patternN  -> expressionN

This way you could define a fibonacci fib function like this:

let rec fib i =
   match i with
      0 -> 0
    | 1 -> 1
    | j -> fib (j - 2) + fib (j - 1);;

Since it’s quite common to define functions with match expressions as their body, Ocaml reserved a special keyword function that defines a function with a single argument treated as pattern match. For example the fib function could be re-defined like this:

  let rec fib = function
     0 -> 0
   | 1 -> 1
   | i -> fib (i - 1) + fib (i - 2);;

The pattern match argument is now implicit in the function definition. That’s quite minimalistic.
There’s also the as keyword used to bind a match to a variable. For example:

let rec fib = function
   (0 | 1) as i -> i
 | i -> fib (i - 1) + fib (i - 2);;

The when keyword evaluates a condition after the pattern match. The fib function could now look like this:

let rec fib = function
   i when i < 2 -> i
 | i -> fib (i - 1) + fib (i - 2);;

Pattern matching can also be applied to other Ocaml primitive types, like strings, chars, Booleans. Let’s take an example of the function is_uppercase:

  let is_uppercase = function
   ’A’ | ’B’ | ’C’ | ’D’ | ’E’ | ’F’ | ’G’ | ’H’
 | ’I’ | ’J’ | ’K’ | ’L’ | ’M’ | ’N’ | ’O’ | ’P’
 | ’Q’ | ’R’ | ’S’ | ’T’ | ’U’ | ’V’ | ’W’ | ’X’
 | ’Y’ | ’Z’ ->
    true
 | c ->
    false;;

But that’s not minimalistic. It would be if we used pattern ranges to specify the list of letters like this:

let is_uppercase = function
   ’A’ .. ’Z’ -> true
 | c -> false;;

The c1..c2 notation specifies the letters range.
The c pattern variable acts here like a wildcard to match all non-uppercase symbols. Since this is another commonly occurring structure, Ocaml reserves the _ symbol to match anything:

 let is_uppercase = function
    ’A’ .. ’Z’ -> true
  | _ -> false;;

Pattern matching everywhere
What’s really interesting about pattern matching in Ocaml, is that not only pattern matching can be applied to (almost) any primitive type, but that pattern matching constructions are also allowed in a variety of places:

let    pattern = expression

let    identifier pattern . . . pattern =  expression

fun    pattern -> expression

So, for example, if we define a tuple like this:

let p = 1, "Hello";;

We could access its components using pattern matching:

let x, y = p;;

Another example with lists could be:

let aList = [1; 2; 3];;
let hd :: tl = aList in
  Printf.printf "%d" hd;;

The latter code will print “1″.

What can’t be done…
We can’t do pattern matching with one type in Ocaml, and that’s functions:

  match (fun i -> i + 1) with
     (fun i -> i + 1) -> true;;
      ^^^
Syntax error

What about JavaScript?
Unfortunately JavaScript doesn’t have pattern matching. The closest thing to pattern matching that JavaScript has is, well, the Regexp object. In some sense that can be regarded as string pattern matching.
The funniest thing about that fact, is that “string pattern matching” can be used as “function pattern matching” in JavaScript.

JavaScript functions have a method called toSource, that returns a string of the source code of the function. For example:

(function (formalParam1, formalParam2)
     { return formalParam1; }).toSource();

should return (at least in Firefox) the String:

"(function (formalParam1, formalParam2) {return formalParam1;})"

However, applying this method to the native Function class returns the following:

"function Function() {[native code]}"

You can find function pattern matching examples in JavaScript when browsing the “Class” object implementations for any JS framework. Most frameworks that provide a “Class” object to wrap prototypal inheritance in something more OO have to check all methods for calls on “super” in order to effectively call the superclass method. However, we first have to verify that the browser supports a toSource method that actually returns a string of the entire function body. That check is done with simple function pattern matching:

  /xyz/.test(function(){xyz;});

This code will return true if the “xyz” pattern is found in the anonymous function. If true, then we can “search” for super calls in all body functions in order to replace them with the proper superclass method call. A nice example of simple JavaScript inheritance implementation can be seen in John Resig’s post

Although I know this is still a regexp match, It can also be seen as comparing a function’s body to a given pattern and thus I believe it can be called function pattern matching.

Weighted nodes, weighted edges

I’ve been doing some work on the JIT lately, fixing some bugs and adding some new features.
Some important changes to mention are:

  • A complete refactoring of the Spacetree. That code was not clear. I also binded the ST to the Animation object used by the Hypertree and RGraph. This allows it to have easeIn and easeOut transitions. I also updated the documentation for this class.
    Main functionality is now packaged under the Tree Class, making a good distinction between generic code (say Tree.Util for example) and more specific code (like the Tree.Geometry object, or Tree.Label).
    Here’s the old example of an “infinite” spacetree, done with the “new code”.
  • Hypertree and RGraphs can now handle weighted nodes and edges in trees and graphs. The same goes for the Spacetree, although I have not tested that yet.

    Weighted nodes:
    This goes only for the RGraph and the Hypertree, since I don’t see a clear representation of weighted nodes in the Spacetree, and the Treemap already represents weight either with size or color.
    Weighted nodes are enabled when setting Config.allowVariableNodeDiameters = true.
    Remember what a dataset is? A dataset is a reference to the property data of a JSON node representation. There you can store data by appending { key:’someKey’, value:’someValue’} objects to the data array property.
    The value of the first object of the data property will be taken in account to calculate the nodes diameters. You will also want to specify two properties of the config object, the nodeRangeDiameters property:

    		//Property: nodeRangeDiameters
    		//Diameters range. For variable node weights.
    		nodeRangeDiameters: {
    			min: 10,
    			max: 35
    		},
    

    Which specifies the specific range of your nodes diameters. Nodes will range from 10px to 35px as default. The other property is the nodeRangeValues:

    		//Property: nodeRangeValues
    		// The interval of the values of the first object of your dataSet.
    		// A superset of the values can also be specified.
    		nodeRangeValues: {
    			min: 1,
    			max: 35
    		},
    

    You’ll need to specify range values for your first dataset object value. This is all the information we need to know in order to plot a RGraph or Hypertree with weighted nodes. Here’s an example of a K6 Hypergraph with variable node diameters (and weighted edges).

    A note on usability
    There’s an extra property for the Hypertree Config object called Config.transformNodes. When applying a moebius transformation of the tree (that is, when the tree moves), tree nodes and edges change their proportion. This is not a good feature if you’re planning to add weighted nodes in your Hypertree, since they will be deformed and thus the user won’t be able to tell which node is bigger than which. You can set this property to false when using weighted nodes on your visualization.

    Weighted edges
    Two new methods have been included in the controller object, these are onBeforePlotLine(adj); and onAfterPlotLine(adj);. Both receive an adjacency object, which has the following structure:

    var adj = {
    	nodeFrom: ""/* A node connected with this adjacence */,
    	nodeTo: ""/* Other node connected with this adjacence */,
    	data: { //An object storing your custom information.
    		weight: w
    	}
    };

    Use the two controller methods to change the canvas lineWidth property or the stroke color (more information on that here). For example,

    /* some code... */
    
     var rgraph= new RGraph(canvas,  {
      	//Use onBeforePlotLine and onAfterPlotLine controller
      	//methods to adjust your canvas lineWidth
      	//parameter in order to plot weighted edges on
      	//your graph. You can also change the color of the lines.
      	onBeforePlotLine: function(adj) {
      			lineW = canvas.getContext().lineWidth;
      			canvas.getContext().lineWidth = adj.data.weight;
      	},
    
      	onAfterPlotLine: function(adj) {
      			canvas.getContext().lineWidth = lineW;
      	},
    
    /* some other code*/
    

    Ok, but how do I store edge weights?
    The JSON Graph structure has been extended to the following form (notice that the old Graph structure is still accepted).

    var json = [
    {
    	"id": "aUniqueIdentifier",
    	"name": "usually a nodes name",
    	"data": [
    	    {key:"some key",       value: "some value"},
    		{key:"some other key", value: "some other value"}
    	],
    	"adjacencies": [
    	{
    		nodeTo:"aNodeId",
    		data: {} //put whatever you want here
    	}
    	//more objects like these...
    	]
    } /* ... more nodes here ... */ ];
    

    JSON Tree structures
    For JSON Trees is simpler. If you have a Node A and B is a child of A, then store in Bs dataset a property concerning the weight of the edge A-B. These nodes will be stored in the adj object onBeforePlotLine and onAfterPlotLine. You can access them by doing adj.nodeFrom and adj.nodeTo.

    Here’s an example of a K6 RGraph with weighted nodes and edges.

  • The last example also shows a new feature for the RGraph, polar interpolation. You will notice that node transitions are different from previous examples. You can change the interpolation by setting Config.interpolation to ‘polar’ or ‘linear’. I’ll make a more detailed explanation for polar interpolation in some other post. If you want to know more about the cool features of the paper inspiring the RGraph, you can see this post.
  • API Changes
    These features introduced an api change that has already been updated in all tutorials, although I have not checked for errors yet (will do today and/or tomorrow). You should not set the controller property from the ST, RGraph, Treemaps and Hypertree instances. That is, you can’t do:

    var st = new ST(canvas);
    st.controller = ; //the controller object
    

    Instead, you should do:

    var st = new ST(canvas, controller);
    
  • I updated all examples packaged with the library, also adding the two K6 examples showed above. Code that depends on the Mootools library (that is, the example files and the Treemap visualization) has been updated to the final 1.2 version of the Mootools library. This library is shipped as an extra with the JIT.

Special thanks to Rene Becker for pointing bugs and Daniel Herrero for suggesting cool performance improvements.
Remember that you can post any question, suggestion or comment on the JIT google group.

Get the library already!
Bye!