Thursday, May 1, 2008

Mutiple representations of an object with Tom mappings

This post shows an example of how Tom object mappings could be used to provide multiple pattern matching representations of objects of the same class.

Introduction

As shown in previous posts, I really like F# Active Patterns and Scala Extractors. Among other things these features allows to create multiple pattern matching representations of objects . One of the ideas behind these concepts is "Views" presented by Philip Wadler in the Views: A way for pattern matching to cohabit with data abstraction paper.

It is possible to use Tom object mappings(explained here) to get a similar effect.

Example

For this example, an alternative representation for a Complex number will be created. A complex number could be represented by Cartesian coordinates (real and imaginary parts) and by Polar coordinates (angle and modulus).

This example is used to present Views, Extractors and Active Patterns.

The Apache Commons Complex class will be used as the complex number implementation.

First, a mapping for the Complex sort needs to be created.


%include { double.tom }

%typeterm Complex {
implement { org.apache.commons.math.complex.Complex }
is_sort(t) { t instanceof org.apache.commons.math.complex.Complex }
equals(t1,t2) { t1.equals(t2) }
}



Then, a mapping for the Complex number with Cartesian coordinates is created. Since the Complex class is in Cartesian coordinates only the getReal and getImaginary accessors are required.


%op Complex Complex(real:double,img :double ) {
is_fsym(t) { t instanceof org.apache.commons.math.complex.Complex }
get_slot(real, t) { t.getReal() }
get_slot(img, t) { t.getImaginary() }
make(real,img) { new org.apache.commons.math.complex.Complex(real,img) }
}


Finally a mapping for the Polar representation. Since the Complex class stores the number in Cartesian coordinates a conversion must be applied to get the angle and modulus slots. Also the polar2Complex method is used to create a Complex instance using the Polar symbol.


%op Complex Polar(m:double,a :double ) {
is_fsym(t) { t instanceof org.apache.commons.math.complex.Complex }

get_slot(a, t) { Math.atan2(t.getImaginary(), t.getReal()) }

get_slot(m, t) {
Math.sqrt(t.getReal() * t.getReal() +
t.getImaginary() * t.getImaginary()) }

make(radial,modulus) {
org.apache.commons.math.complex.ComplexUtils.polar2Complex(
radial,
modulus) }
}



A use of these mappings is the following:


Complex c = new Complex(3,3);

%match(c) {
Polar(m,a) -> {
System.out.println(
String.format("%f,%f",`m,`a ));
}
}


Also given that a make declaration was added to the mappings we can use the object creation syntax as follows:


Complex c2 = `Polar(3,Math.PI/2.0);

%match ( c2){
Complex(r,i) -> {
System.out.println(
String.format("%f,%f",`r,`i));
}
}