[JSR308] A simple proposal for recursive annotations

Lex Spoon lex at lexspoon.org
Tue Jun 16 14:49:08 EDT 2009


On Jun 16, 2009, at 12:12 PM, Dr. James J. Hunt wrote:
> As for using Java syntax for annotations, that is perhaps desirable,  
> but
> more difficult to implement.  You example is too short for me to see  
> if
> what you intend would work for a specification language.  It would be
> nicer to write something like
>
> @Spec("\require i >= 0 && i < a.length();
>       \ensure \result == elements[i];")


My proposal was to embed the expressions directly, rather than in a  
string.  That way, it's the Java compiler that does the parsing and  
type checking.  It already has subroutines for that, so it can easily  
do so.  The annotation processor is then provided an abstract syntax  
tree.

Presuming annotations worked that way, you could write the above spec  
like this:

@Spec(require(i >= 0 && i < a.length()), ensure(result == elements[i]))

Doesn't that look sufficient?


Note that several things must be in scope for this example to work,  
but it doesn't seem like any trouble.

1. "require", "ensure", and "result" must be in scope.  They could be  
imported from static members of a utility class.

2. "i" and "a" must also be in scope.  This initially struck me as  
weird, when I was first thinking about these problems for Scala, but  
over time I came around to allowing it.  It's just too common to want  
to annotate a method and talk about it's parameters.


> Using Java like syntax as you propose does not address the issue of
> subtype refinement either.  There are interesting uses for numerical
> calculation checking when one can tag the units used and understand
> their relationship.  For example, if @Meter and @Square are both
> subtypes of @UnitExpression, then one could check things like whether
> the cast below is correct or not, even at the byte code level.
>
> public @Unit(@Square(@Meter)) int area(@Unit(@Meter) int length,
>                                       @Unit(@Meter) int width)
> {
>  return (@Unit(@Square(@Meter)) int)length * width;
> }
>
> Mixing units would be easy to catch.

That way works.  However, if you use expressions instead of nested  
annotations, you already get inheritance for free, which is an example  
of what I'm getting at.  If the system makes the single decision to  
reuse the expression language and its type system, then a lot of  
useful expressiveness is inherited (so to speak) by the annotation  
language.

It so happens I have tried out this approach on units, and it  
certainly works for basic examples like the above area method.  By the  
way, I really do like the units example.  It's statically checkable,  
but you can't check it with standard type systems.  Here's the source  
code for my proof of concept:

http://lampsvn.epfl.ch/svn-repos/scala/compiler-plugins/units/trunk

Here is a blog article by someone else showing what it's like to  
install and use the system; it looks similar to JSR 308's goals:

http://www.michaelnygard.com/blog/2009/05/units_of_measure_in_scala.html


In this units system, I used Andrew Kenedy's terminology.  He calls  
9.8 m/s^2 a "measure", whose "scale" is 9.8 and whose "unit" is m/ 
s^s.  In turn, the unit m/s^s has a "dimension" of length/time*time.   
Given this terminology, a "units checker" is really a checker of  
*dimensions*.

To see what the annotations look like, here is a simple example from  
StandardUnits.scala:

  val kg : SimpleUnit @dim(Mass) =  SimpleUnit("kg", Mass, 1.0E3)
This says that the kg unit is of type SimpleUnit @dim(Mass).  This  
type has an annotation, @dim(Mass).  The body of that annotation is an  
arbitrary Scala expression, in this case simply "Mass".  That  
expression could also be usefully evaluated at run time, although  
that's a choice that must be made for any library of annotations.

Here's a more complicated example:
  val J: UnitOfMeasure @dim(Length*Length*Mass/(Time*Time)) =  
unitOf(N*m)

This time, the dimension is Length*Length*Mass/Time*Time.  Again, this  
can be evaluated at a Scala prompt, and similar evaluations take place  
for the run-time checks that the system also allows.

I see no reason this wouldn't work in Java.  It's a simple  
rearrangement of the above code to yield the same thing in Java:
   public static final SimpleUnit @dim(Mass) kg = SimpleUnit("kg",  
Mass, 1.0E3)
   public static final UnitOfMeasure  
@dim(Length.times(Length).times(Mass/(Time.times(Time)))) = unitOf(N *  
m)


Likewise, the area method converts fine:

public Measure @dim(Length.times(Length)) area(Measure @dim(Length)  
length, Measure @dim(Length) height) {
   return length * height;
}

Granted, you might want to tweak the language of dimensions for Java.   
For example, instead of the ugly times() method, you might add a  
square() method:

public Measure @dim(square(Length)) area(Measure @dim(Length) length,  
Measure @dim(Length) height) {
   return length * height;
}



What do you think, James?  Does it look sufficient for units checking  
to allow expressions in the annotations?


Overall, I had meant just to make a little suggestion, but I confess  
this is turning into a real proposal.  Where do things stand now?  If  
there is really pressure to extend the annotation language to support  
things like preconditions, postconditions, and units, then I confess I  
really do propose using Java expressions.  It looks much better to me  
than the alternative of adding multiple extensions to the annotation  
language and still not having as much expressiveness.  I would be  
happy to help out, for example by contributing a reference  
implementation, but I will wait to hear where things are before  
putting any more time into it.


Lex

-------------- next part --------------
An HTML attachment was scrubbed...
URL: https://lists.csail.mit.edu/pipermail/jsr308/attachments/20090616/a589b132/attachment.htm 


More information about the JSR308 mailing list