Looking for Prism XE4?
Read more about the future of Oxygene, here.
RemObjects Software
Home | Products | Trials | Shop | Support | About |
 
   

The Special

This page dives into some of the major features of the Oxygene language that make it special and set it apart from other regular modern languages, but are still fairly common and well-known concepts.

Class Contracts

Class Contracts were one of the major new features introduced to Pascal back in the first 1.0 release of Oxygene. The idea behind class contracts it to write code that is "self testing", by documenting certain assumptions and expectations as part of the code itself, in ways that the compiler can (optionally) enforce.

Class Contracts provide two distinct language features that work together to achieve that:

"require" and "ensure" clauses

Traditional Object Pascal method blocks are made up of a "method"/"begin"/"end" block that contains the execution statements that make up a method. Oxygene of course continues to honor that syntax, but extends a method implementation by allowing two optional sections:

A "require" section that precedes the actual code and can define a set of requirements or assumptions that the method makes in order to execute successfully. These could be verifications of input parameters, or checks on the global state of the containing object (if, say, the method is only valid to be called under specific circumstances).

An "ensure" section that follows the main code block, and can similarly encode assumptions about the state of things after the method has exited.

Both of these sections can contain a list of boolean expressions that are expected to be true. If the "assertions" compiler option is enabled (the default for debug builds), the compiler will automatically generate code to verify each of these expressions, and throw an assertion if they are violated.

Inside "ensure" expressions, the optional "old" qualifier can be used to refer to values as they existed before the method ran — allowing the code to do before/after comparisons.

method Foo.Inc(aIncBy: Int32);
require
  aIncBy > 0;
begin
  fValue := fValue + aIncBy;
ensure
  fValue > 0;
  fValue - aIncBy = old fValue;
end;

Invariants

Different to the "require"/"ensure" sections, invariants define expectations that need to always be true on an object level. Once again, the checks can be encoded in a list of boolean expressions, and the compiler will automatically add code to the application to enforce those invariants whenever necessary.

Invariants come in two flavors:

"public invariants" are declared in a section of the class that starts with these two keywords, and they define expectations that must be true whenever public (or in reality, non-private) methods complete. That is, these invariants may be broken for a tiny fraction of time between internal method calls, but must eventually rectify themselves before control is returned to the caller of the object.

"private invariants" must be true at the end of every method call, even inside call chains that are internal to the class.

type
  MyClass = class;
  public
    ... some methods or properties
  public invariants
    fField1 > 35;
    SomeProperty = 0;
    SomeBoolMethod() and not (fField2 = 5);
  private invariants
    fField > 0;
  end;

Together, invariants and require/ensure clauses let you create code that is more robust and will notify you of method calls with bad parameters or inconsistent object states early on. At the same time, because all assertions can be turned off with a single compiler switch, the presence of Class Contracts doesn't need to have any performance impact on production builds.

Duck Typing

The name Duck Typing comes from the old saying that if something walks like a duck and quacks like a duck, it is a duck – and applies the same concept to objects.

Imagine you have multiple types that share a set of common methods, but no common ancestry. Both Objects might have a Draw() method, but because they both introduce the method independently, you have no way to leverage polymorphism and call the same method on either class without explicit type casting.

With duck typing, you can work with any object as if it were compatible with a given interface type (say, IDrawable), as long as the object implements the necessary methods, for example:

var x := duck<IDrawable>(new Circle);
x.Draw();
x := duck<IDrawable>(new Gun);
x.Draw();

In the example above, "x" will, in turn, hold two completely independent objects, but through the magic of duck typing, the Draw() method of either type can be called on "x".

Soft Interfaces

To make duck typing even more interesting, the concept of "soft interfaces" has been added to the language. Going back to the sample before, by simply declaring a regular interface IDrawable, we are able to duck-type either type to that interface.

If we go a step further and declare the interface as "soft", such as:

type
	IDrawable = soft interface
    method Draw();
  end;

then the compiler would go the extra step and automatically treat any object with a matching Draw() method as assignment compatible with IDrawable — without the need for explicit calls to the duck() function.

Mapped Types

Mapped Types are a relatively new feature introduced in Oxygene 5.1.

Mapped types allow the definition of "virtual" classes or interfaces that map to existing types in the framework or your own code, exposing them with a different API. One great use for mapped types is the ability to expose similar classes with slightly different APIs in a common fashion: for example the java.lang.Dictionary class in the Java framework could be mapped to look like the System.Collections.Generic.Dictionary class provided by .NET, so that common code can be written to run on both platforms.

The "Sugar" cross platform library that we are working on for Oxygene 6 will be heavily based on mapped types.

Cirrus

Cirrus is a combination of language feature and class library that allows you to apply concepts of Aspect Oriented Programming to Oxygene.

The idea behind AOP is the so-called "separate of concerns", in that functionality that is orthogonal to the core functionality of a class is implemented elsewhere and then "injected" into other classes as needed, as an aspect.

Three common examples for this are logging, transaction handling or security. It is often helpful to have detailed logging capabilities in a class library, but at the same time, it can convolute the code if every single method you implement also has to contain logging — and things get even more complex if several of these concepts should be added to the same class.

With AOP, the actual classes and their methods can concentrate on just doing their job. Separately implemented "aspects" can be applied to the class, with a simple extension to the attributes syntax:

type
  [aspect: Logging]
  Foo = class
    …
  end;

and the aspect can automatically take care of "injecting" the necessary logging code into every method that needs it.

We recommend that you read more about AOP and Cirrus on the Oxygene Language Wiki.