Search

Rss Posts

Rss Comments

Login

 

Class Types and Invariants

February 22, 2009

Right off the bat, I should say I don’t expect this project to be used in any real-world applications, as the amount of overhead is substantial and performance could suffer, so use at your own risk. Instead, this project is about demonstrating the versatility of JavaScript; it is common practice these days to build on top of the language and extend upon its native capabilities due to a lack of support for various functions and implementations found in other languages. This has lead to many of the countless hacks and extensions found in most modern day libraries, such as inheritance, native object extensions (Array), dynamic script injection, animation, and CSS selector engines to name a few. Now you can add one more to the list; class type and invariant declaration and validation.

Aurora

The goal of the project is to provide a means of defining instance variables bound to a specific data type as you would find in Java or a variety of other languages. All subsequent calls to the methods of the instance result in automatic variable validation after the method call is complete. If a type violation is found, a descriptive exception is thrown and that specific variables value will be reverted back to what it was before the method call took place.

In addition to that, there is added support for class invariants, inspired by Eiffel, each class can declare a set of invariants for each instance variable which outlines the rules of that variable. For example, a variable called month in the class calendar must be an integer between the values of 1 and 12, if that rule is broken an exception is thrown.

Data Types

By default, there are 12 declared data types; String, Date, Number, RegExp, Function, Boolean, Array, Object, Null, Element, Event, and NodeList. In addition to that, a variable can not only be declared as type Array, but the contents of the Array can also be constrained to a specific data type:

items: [Object]
collection: Array(Element)

Also, any declared class will also automatically become a declared data type, meaning a class can define a variable to have the data type of another class. For example, a Menu class may have a variable assigned to hold an Array of instances of the class Menu.Item:

menuItems: [Menu.Item]

The define method allows you to add your own additional custom data types for the purpose of variable declaration. The method requires only two arguments; the name of the new data type as a string which does support namespacing and a function that will be used to validate all variables against.

Aurora.define('Hash', Aurora.isHash);
Aurora.define('Integer', Aurora.isInteger);
Aurora.define('Decimal', Aurora.isDecimal);

Without initialing them, there is a variety of functions available to allow you to easily add additional support for a variety of different data types. Almost all objects often encountered within Javascript have an associated validator included in Aurora to enhance the data type declarations, validators include:

  • Aurora.isHash
  • Aurora.isInteger
  • Aurora.isDecimal
  • Aurora.isXML
  • Aurora.isXMLDocument
  • Aurora.isXMLNode
  • Aurora.isWindow
  • Aurora.isDocument
  • Aurora.isNode
  • Aurora.isArrayLike
  • Aurora.isVoid
  • Aurora.isNative

Invariants

The class invariant is used to set constraints on specific variables to prevent unwanted data from being assigned to an instance variable. All invariants are defined in an invariant object, each invariant is assigned the same name as the variable they represent and are to constrain. They must take the form of a function and it also must return a boolean; true if the variable is valid and false if it is not. The following example demonstrates class invariants you might find in a calendar class:

invariant: {
    day: function(){
        return this.day >= 1 && this.day <= 31;
    },
    month: function(){
        return this.month >= 1 && this.month <= 12;
    },
    year: function(){
        return this.year >= 1 && this.year <= new Date().getFullYear();
    }
}

Classes

The declare method is used to define a new class and has a similar syntax to the dojo.declare method. The method can accept either two or three arguments depending on whether the new class will inherit from a superclass. The first argument is a string name of the class which also supports namespacing, if there is a superclass it will become the second argument, otherwise the property object will follow. The property object is where the constructor, all declared data types, methods, and the invariant will be declared. Both the constructor and the invariant are not required, additionally, any properties that are not declared with a data type will be treated like a typical property and not subject to type checking.

Aurora.declare('Clock', {
 
    hour: Number,
    minute: Number,
    second: Number,
 
    constructor: function(hour, minute, second){
        this.hour = hour;
        this.minute = minute;
        this.second = second;
    },
 
    invariant: {
        hour: function(){
            return this.hour >= 1 && this.hour <= 12;
        },
        minute: function(){
            return this.minute >= 0 && this.minute <= 59;
        },
        second: function(){
            return this.second >= 0 && this.second <= 59;
        }
    }
});

Inheritance

Classical inheritance is also supported and maintains the same behaviour and functionality that you would expect. However, in addition to the inherited methods and the ability to call superclass methods, both declared types and invariants of a superclass will also be inherited by all sublcasses. Any defined invariants with the same name as an invariant inherited by a sueprclass will automatically be overwritten. However, they maintain the ability to call the invariant of the superclass method for additional validation.

Aurora.declare('Component', {
 
    value: Number,
 
    constructor: function(value){
        this.value = value;
    },
 
    invariant: {
        value: function(){
            return this.value > 0;
        }
    }
});
 
Aurora.declare('Module', Component, {
 
    name: String,
 
    constructor: function(name, value){
        this.name = name;
        Module.superclass.constructor.call(this, value);
    },
 
    invariant: {
        name: function(){
            return this.name != "";
        },
        value: function(){
            return this.value < 10 && Module.superclass.invariant.value.call(this);
        }
    }
});

Exceptions

Whenever a violation occurs, whether it be a type or invariant violation, an exception is thrown that indicates the type of violation, the class and method in which the violation occurred, the variable that failed the validation, and in the case of type violations the expected type. For example, a type violation in Firefox’s error console looks like so:

type violation

Likewise, a violation on a variable with type Array with constrained contents will produce this error message:

array violation

Finally, if any variables break the contraints set by its invariant, an exception will be thrown:

invariant violation

Example

The example demonstrates a typical type violation and the exception thrown when one occurs. Click here to see it in action and be sure to open your error console.

Summary

As I mentioned in the beginning of the article, Aurora is a demonstration of the flexibility of JavaScript and not intended for serious use. Like many of the mainstream libraries available today, hacks and extensions are the name of the game, and there is almost nothing that can’t be simulated or imitated in one fashion or another. There is still much room for improvement within Aurora, the next most logical improvement would be adding validation to both method arguments and return values. No matter where Aurora goes from here, the most I can hope for is that other developers more creative and skilled than me can provide their own work of magic to the community.

Download

3 Comments

  1. Feb 26 at 2:21 PM

    [...] has created a really fun experiment that shows the versatility of JavaScript. He just announced Aurora, fun times with class types and invariants in [...]

  2. Feb 26 at 7:26 PM

    [...] has created a really fun experiment that shows the versatility of JavaScript. He just announced Aurora, fun times with class types and invariants in [...]

  3. Mar 01 at 10:52 AM

    Ryan Morr – Javascript, CSS, and Web Apps » Class Types and Invariants…

    Thank you for submitting this cool story – Trackback from DotNetShoutout…

Post a comment