Wednesday, April 04, 2012

Xtend Example : Working with Distances


This is an example showing how to do arithmetics with distances in Xtend. It uses operator overloading and extension methods
Here’s what we want to be able write:
class HelloDistance {
  @Test def void testOperators() {
    assertEquals( 23.cm , 10.mm + 22.cm )
    assertEquals( 3.km , 1.km * 3 )
    assertEquals( 1_019_99_7.mm, 1.km + 20.m - 3.mm )
  }
}
The first thing we need to do in order to be able to create distances like shown in the example above, is to define extension methods for the type int.
We create a new class, call it Distance and add the following static methods:
class Distance {
  def static mm(int millimeters) {
    new Distance(millimeters) // not yet existing
  }
  def static cm(int centimeters) {
    mm(centimeters * 10)
  }
  def static m(int meters) {
    cm(meters * 100)
  }
  def static km(int kilometers) {
    m(kilometers * 1000)
  }
}
In order to add the static methods as extensions to the type int, we have to add the following import declaration in our test class:
import static extension my.example.Distance.* 
As you can see we call a not yet existing constructor which takes the distance in millimeters as an int. So let’s add the missing constructor and add the corresponding field to the Distance class:
int mm
new(int mm) {
  this.mm = mm
}
Since this is an immutable value object we might also want to have a proper equals and hashcode implementation:
override equals(Object obj) {
  switch obj {
    Distance : this.mm == obj.mm
    default : super.equals(obj) 
  }
}
override hashCode() {
  this.mm
} 
Finally we need to add definitions for the arithmetic operators we want to use:
def operator_plus(Distance other) {
  return new Distance(this.mm + other.mm)
}
def operator_minus(Distance other) {
  return new Distance(this.mm - other.mm)
}
def operator_multiply(int times) {
  return new Distance(this.mm * times)
}
That’s it. It’s just API definition, and of course since Xtend is statically typed the tooling will guide you when using it.

7 comments:

  1. I like the syntax but it would be nice if this worked with more complex formulas too. But this would require a more powerful typesystem, I think.

    example 1:

    val dist = 10.m
    val time = 2.sec
    val speed = dist / time // == 5 m/sec
    // how much is this in one hour?
    val dist 2 = speed*1.hour // == 18km

    example 2:

    val x = 3.m
    val y = 5.m
    val dist = sqrt(x*x + y*y)

    ReplyDelete
  2. It may help using Unit-API or Eclipse(!) UOMo on top of that.

    The syntax is very Groovy-like and Groovy co-founder Guillaume Laforge wrote about something similar but more generic based on Groovy and JSR-275 (antecessor to Unit-API) a while ago: http://groovy.dzone.com/news/domain-specific-language-unit-

    ReplyDelete
  3. @Werner I don't see why you consider the Groovy approach more generic?
    After all in Groovy it relies on global class manipulation at runtime. I.e. no feedback or tooling at development time but possible strange side effects in the whole system. Not so nice...

    ReplyDelete
  4. I was refered mostly to the usage of an accepted and working unit library like JSR-275 and successors like Unit-API by the Groovy approach, Guillaume presented. According to the article, it was also used by an actual healthcare solution, not thus not just for the demo or in his blog.

    If Xtext could make better use of such libraries, that sounds great.
    With a powerful typesystem and unit library underneath, calculations like the initial poster "peq" suggested should be quite easy.

    Hope to discuss how it could be used at some of the upcoming DemoCamps, like Hamburg or Vienna;-)

    ReplyDelete
  5. I was refered mostly to the usage of an accepted and working unit library like JSR-275 and successors like Unit-API by the Groovy approach, Guillaume presented. According to the article, it was also used by an actual healthcare solution, not thus not just for the demo or in his blog.

    If Xtext could make better use of such libraries, that sounds great.
    With a powerful typesystem and unit library underneath, calculations like the initial poster "peq" suggested should be quite easy.

    Hope to discuss how it could be used at some of the upcoming DemoCamps, like Hamburg or Vienna;-)

    ReplyDelete
  6. What i don't like about this is that it doesn't seem to be type safe, at least not at compile time. When i define the type distance, instantiate one like so 5.mm and compare it with, say, an int the compiler won't complain about it:

    5.mm > 5

    And it will only fail at runtime if i specifically define it to do so in my compareTo method (or if i just cast the type in compareTo without checking the type)

    ReplyDelete
    Replies
    1. I don't know what you mean, since It is statically typed checked.
      It depends on the signature of the 'operator_greaterThan'-method whether it accepts Distance and int or not.

      Delete