Sunday 20 April 2014

Constraints in Dart

I've been tinkering with Google's Dart for a while now. Initially, what attracted me was that it ran in the browser and sucked a lot less than JavaScript, which admittedly is not very hard.

As I came to use it more, I soon realised it sucked a lot less than Java too and ever since I've been hooked.

Recently I decided the time had come to revisit an old friend...

Constraints

Constraints and I go a long way back. 

The problem of how to best capture business rules once and then share them everywhere you needed them, became a particular focus of mine and some of my colleagues back in the early naughties, when I was working as a Senior Java Architect for Sun Microsystems Professional Services.

One of the things we struggled with was that the constraints were effectively meta data but Java had no way to extend the meta data of classes. That was until ...

Annotations in Java are born

Whilst attending a talk on annotations at JavaOne in 2004, it dawned on me that this is exactly what we'd been missing.

When I returned and had some bench time I created a library that used Annotations to express constraints on objects and validate them. 

In march 2005 I published an article about the library on java.sun.com Using Annotations to add Validity Constraints to JavaBeans Properties as well as my hopes for it becoming a standard in Java. (Sadly this link is now broken so you can't get to the article - thanks Oracle).

Sometimes wishes do come true

A few years later I was working at an investment bank, when a colleague said to me "hey isn't that what you wrote your article about a few years ago". It was the first release of the Java Beans Validation (or JSR 303). What's more it was remarkably similar to the code I had attached to the article, which was rather flattering (even though I didn't get any credit - not that I'm bitter about that ;-))

Constraining Dart

So wind the clock forward to now (yeah I know I'm old) and I thought I'd take a crack at writing a constraint library in Dart. The result of my labours is the constrain package of which I just released what I hope is the first workable version.

Even though I knew this was going to be simpler in Dart, I was still struck by how much simpler and how much more powerful the end result is.

So what makes it simpler and better?

1. Dart's Annotations suck a lot less that Java's

Java's annotations are like some watered down version of an interface. Limited to purely providing a bit of data.

In contrast, Dart allows you to use any class as an annotation as long as it can be made a compile time constant.

This means it can have methods and use inheritance, mixins etc. which is friggen awesome. In my constrain library, the core constraint class looks like

abstract class Constraint<T> {
  final String description;
  final ConstraintGroup group;

  const Constraint({ this.description, this.group });

  void validate(T value, ConstraintValidationContext context);
}
So unlike in Java Bean Validation, the Constraint class has a validate method so you don't need to define this separately.

This simple difference means that, where in Java you need two classes (an annotation and a validator) in Dart you need just one that covers both.

2. Dart has Functions

Yeah I know Java 8 has got Lambdas, so maybe it can learn a few new tricks now. In Dart, functions are true first class citizens. This allows for my next trick. 

You can define new constraints by simply passing a validating function. So not only do you not need a separate validator class, you don't even need to subclass Constraint to create a new constraint.

In constrain this is provided by a subclass of Constraint called Ensure. The following shows an example of using Ensure to constrain that a person can't be their own parent (at least not with current science).

class Person {
  @Ensure(cantBeYourOwnParent,
      description: "A person cannot be their own parent")
  Set<Person> parents;
}

The validation for the constraint is provided by the following function.

bool cantBeYourOwnParent(Set<Person> parents, Person person) =>
    !parents.contains(person);

In Java this would have required a new constraint annotation plus a new validator class.

This opens up adding constraints in a very straightforward manner, with the power of Dart at your disposal.

3. Dart has a cool Matcher library

The authors of Dart's unittest lib had the wisdom to split out the matcher's in to a separate library

It turns out that matchers are a pretty good fit for constraints as they are designed to provide a description of what went wrong. This can be useful in providing a reason for the constraint violation.

To illustrate we add an age property to the Person class as follows

  @Ensure(isBetween10and90)
  int age;
This time we use a matcher function to define the validator for the constraint
Matcher isBetween10and90() =>
    allOf(greaterThanOrEqualTo(10), lessThanOrEqualTo(90));

All the things

So in Dart you can reap all the benefits of defining constraints without the tedious boiler plate you have in Java. So go forth and constrain your Dart...