Pascal is more relevant today than ever, and modern Pascal implementations such as Oxygene have a lot to bring to the table.
Oxygene is a powerful general purpose programming language, designed to let developers create all imaginable kinds of projects on a wide variety of platforms.
To achieve this, it provides a combination of language features that ease the development processes — from basic Object Oriented language concepts found in most modern languages (such as the concept of classes with methods, properties and events) to sophisticated specialized language features that enable and ease specific development tasks (such as creating safe, multi-threaded applications) — many of those unique to Oxygene.
All of the provided features are based on the foundation of Object Pascal and stay true to the language design paradigms that make Pascal great, readable and discoverable.
Take a closer look at the features that make up the Oxygene language:
THE BASICS This section covers some of the basic constructs that make up the Oxygene language. If you are familiar with Object Oriented software development, you won't find a lot of mind-blowing things here, but you will get a basic introduction to the Object Pascal syntax. Even if you are familiar with Pascal already, you might find some interesting syntax variations and details.
Classes & Interfaces | Types | Methods, Statements & Expressions | Namespaces
THE MODERN This section covers more modern OOP constructs as available in Oxygene. Most of these might be familiar to you from other modern languages, such as C# or Java, and this section will show you how these concepts apply to Oxygene specifically.
Generics | Sequences & Queries | Tuples
THE SPECIAL This section dives into some of the less mainstream features of the Oxygene language. Many of these are inspired by features from other academic or more specialized languages, and you will learn how Oxygene brings these features into Pascal and makes them accessible for modern application development.
Class Contracts | Duck Typing | Mapped Types | Cirrus
THE AWESOME Last, but certainly not least, this section takes a look at some of the awesome features that make Oxygene unique. Many of these features exist only in the Oxygene language, or are uniquely applied to Oxygene in ways beyond what other languages offer.
Nullable Types & Expressions | Futures & Parallelism
THE LITTLE THINGS Oxygene contains a myriad of small little details that make writing code a joy. Individually, none of these constitute major features, but they give the language the finishing touches. After all, it's the little things that count.
Oxygene is a fully object oriented language that lets developers write code that runs on the .NET/Mono, Java/Android or Objective-C Runtime Environments, respectively. It is based around and builds on the language structures of Pascal and Object Pascal.
Oxygene generates code that uses Garbage Collection (GC) on the .NET and Java runtimes and Automatic Reference Counting (ARC) on the Objective-C runtime. This means that regardless of platform, the management of object life times is taken care of without explicit code needed from the developer, although the exact underlying semantics may differ slightly.
As an object oriented language, most code written in Oxygene lives in "Classes". Classes are an amalgamation of data and related functionality, and can consist of fields, methods, properties and — on .NET — multicast events.
Oxygene classes support all features familiar from other modern development languages: inheritance allows classes to descend from one another, extend each other, and be used polymorphically. This is extended by support for interfaces, which allow unrelated classes to implement common well-defined sets of APIs to be used in a consistent manner. Of course, classes can be abstract (i.e. need to be inherited from to become useful) and sealed (i.e. prohibited from being extended further).
type
MyBetterButton = class(Button)
…
end;
Class members, such as methods, properties and events, can have different visibility scopes, controlling how they can be accessed from within the class or externally from other classes. Properties can have different visibility scopes for read and write access, respectively. Members can be marked virtual so they can be overridden in descendant classes — one of the cornerstones of polymorphism.
In addition to members, classes can also define invariants that will help with proving the consistency of the class during runtime. Invariants are boolean statements that describe the valid state of each object, and if present, the compiler will automatically ensure that invariants will be validated whenever necessary, detecting invalid object state and programing errors that cause it early on. Invariants are one half of Oxygene's implementation of Class Contracts.
Individual instances of classes are referred to as "Objects".
The bulk of code in a project exists within the bodies of a class's methods. A method body is surrounded with the "begin" and "end" keywords, and can contain a list of statements, separated by semicolons, that will be executed in sequence when the method is called.
In addition to simple statements, such as calls to other methods, assignments or arithmetic expressions, Oxygene of course provides a wide range of code flow statements to express application logic, such as for/to, for/each, while, repeat/until and infinite loops, "if" and "case" statements, assignments and of course method calls.
Expressions are a certain subset of statements that are considered to have a value that can be reused or built upon. For example, in the following to statements:
var x := 5 + 9;
(5 + 9).ToString;
the "5 + 9" statement is an expression that applies the "+" operator to the values "5" and "9". The result of that expression can be used further, for example to be assigned to the variable "x", or to have the "ToString" method called upon.
Not all statements are expressions, so the following code is not valid:
var y := Console.WriteLine("Hello");
because the call to "Console.WriteLine" is a statement without (return) value.
The type system in Oxygene is rooted, that means every type ultimately descends from the common base class, "Object"(1). Oxygene automatically boxes and unboxes so called simple types — such as Integers — as necessary, making the distinction between objects and simple types irrelevant to the Oxygene developer for most cases. Oxygene also supports the concept of "nullable" simple types. Nullable types are fully supported in expressions.
Oxygene also supports a range of intrinsic types that are not classes in the strictest sense — although instances of them can be treated as objects. These include "Enums" to define a group of distinct related values, "Arrays" that contain a numbered list of elements accessible by index, and "Sequences" which similarly contain a list of elements that can be accessed sequentially but not by index.
Oxygene is a strongly-typed language by design, meaning that type safety is enforced and method calls are validated as compile-time. At the same time, it provides powerful features that let code break out of the confines of the safe type system, if needed. These include duck typing and soft interfaces which allow compatible objects to be used polymorphically without having a common ancestor, and the dynamic type, which allows arbitrary (checked at runtime) method and property calls on objects whose type is not known at compile time.
Oxygene identifies types by a unique combination of their name, and an (optional) namespace prefix. By default, a type's namespace is defined by the global namespace name declared for the file that the type is defined in, but individual type declaration can override the namespace by providing what is referred to as a "fully qualified type name":
namespace Foo; // default "current" namespace for types in this file
type
MyType = class … end; // "Foo.MyType"
…
Bar.MyOtherType = class … end; // "Bar.MyOtherType"
When referring to (i.e. using) types, their names can be specified using the full name, or the short name can be used if the type exists in the "current" namespace or a namespace referenced in the "uses" clause:
namespace Foo;
…
uses Bar;
…
var x: MyOtherType; // finds "Bar.MyOtherType"
(1) with the exception of C-style types accessible on the Objective-C Runtime.
This page dives into some of the features that make Oxygene a modern programming language for the early 21st century. These are not features that make Oxygene unique, and they are in fact shared with most of todays modern languages, but nonetheless they are features you do not want to miss in your day-to-day programming.
Generics are a core feature of modern languages today, and Oxygene provides full support for them on both .NET and Java.
Simply put, generics allow the developer to declare classes that make strongly-typed use of other types, without knowing the exact type at the time of implementation.
A common example is a List class, which can be used in different incarnations to hold different types — for example a list of Integers, a list of strings or a list of custom types called Foo.
Rather than implementing IntegerList, StringList and FooList separately (duplicating a lot of code) or falling back to a plain list of untyped "Objects" (which loses type safety and requires unnecessary casts when accessing elements) a generic List
Throughout the implementation of List
Users of the class can instantiate concrete versions of a generic simply by specifying an actual type name, e.g.:
var x := new List<Int32>;
var y := new List<String>;
and when working with these lists in "x" and "y", they can be accessed with the safe knowledge that all members of the list are Int32s or Strings, respectively.
Sequences are a special type in the language and can be thought of as a collection of elements, similar to an array.
In contrast to arrays, sequences do not imply a specific form of data storage, but can represent any collection of elements that is accessible in a specific order. This could be an array (and as a matter of fact, all arrays can be treated as a sequence) or a different data store, such as a linked list, a binary tree or a custom collection implementation.
Sequences can also represent non-static data that is retrieved or generated on the fly, as the sequence is enumerated. For example, one could implement a sequence that calculates all digits of Pi, or retrieves RSS headlines downloaded from a server.
In Oxygene, sequences are represented by the "sequence of" keyword (analogous to the "array of" syntax), so a variable holding a sequence of strings would be defined as follows:
var Names: sequence of String;
A variable defined like this would then be capable of referring to any kind of sequence — be it an array, a standard generic List
The main power of sequences arises when used in combination with Query Expressions, also known as LINQ.
Query Expressions provide an SQL-like syntax to perform actions or apply filters to sequence types — for example to narrow down a sequence to all elements with a particular property value, or to sort a sequence using a property:
var byLength := from n in Names order by n.Length;
Oxygene 5.2 also extends the Oxygene language with support for native tuple types for both .NET and Java. Tuples are a new core type in the language, like arrays or sequences, and are designed to hold a set of specific typed objects.
Tuples make it easy to pass around groups of values (for example a String and an Integer), store them in arrays or collections, even return multiple values from function calls.
You can define a tuple variable such as:
var t: tuple of (String, Int32, Boolean);
and you can access individual members of the tuple using the strongly typed [] indexer:
var s := t[0];
var i := t[1];
where each index is strongly typed, i.e. the compiler knows that t[1] is an Int32 type. You can also declare tuple literals using the new () syntax:
t := ('Holla', 23, false);
and even assign tuples back to individual values:
var s: String;
var i: Int32;
(s, i) := MyTupleMethod();
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 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:
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;
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.
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".
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
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 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 Docs Site.
This page focuses on areas in the Oxygene language that are not just "modern" or "pretty unique", but, in our opinion, really set Oxygene apart from the rest and give developers that extra boost of productivity and flexibility that lets them create truly great applications.
Nullable (simple) types are a common feature on the .NET runtime, available in all languages, but Oxygene takes their language integration to the next level by making them first class citizens.
To the runtime, a nullable type, for example a "nullable Int32", behaves nothing like a real Int32, because it is, in reality, a wrapper around the actual type that provides indirect access to the value (if present) and a flag to check if the type is nil or not. That is not very intuitive to use.
In Oxygene, a nullable Int32 behaves just like a regular Int32 — except of course that it can be nil. Consider the following code:
var x: nullable Int32;
var y := 5; // y is a regular Int32;
var a := x + y;
x := 4;
var b := x + y;
At the time of initialization for "a", the variable "x" has not been initialized, so it is nil. Still, it can be used in the "x+y" expression to calculate a value. Oxygene will apply common nullable expression logic, whereas "nil + 5" equals nil, so "a" will be considered a "nullable Int32" as well, and its value will be nil.
When the code gets to "b", "x" has been initialized. The type of "b" will still be inferred as a nullable Int32 (because the entire "x+y" expression is considered nullable), but the value assigned to "b" will be "9", of course.
This native treatment of nullable types extends throughout the entire language; for example "x+y" could be passed to a method expecting a nullable Int32, and "if" statements and "while" loops will accept nullable Booleans (such as "if x+y = 0" then …) transparently.
The deep integration of nullable types is also what enabled the small but powerful Colon (":") Operator.
The Oxygene language introduces a wide variety of language constructs to make it easy to write multi-threaded code — a feature that is becoming more and more important as CPUs cease to become much faster, but increase in the number of cores for parallel execution instead.
One of the most exciting and powerful features in the Oxygene language are future types, or more precisely, asynchronous future types.
By declaring a variable to be of type "future X" (where "X" can be any valid type), the developer is specifying that the variable will hold a value that is not known yet, but will be determined at some later point in time, ideally asynchronously.
What makes future types powerful is that they behave just like the result type they represent, and can be used naturally and in expressions. Consider for example:
var x: future Int32 := async CalculateSomethingSlow();
var y: Int32 := SomethingElse;
…
var z := x + y;
In this code snippet, the first line defines the variable "x" and how its value will be determined, without having to wait for the CalculateSomethingSlow (which presumably will be really slow) to finish. The code can run on to call SomethingElse and do further work. It's not until he last line, when "x+y" is evaluated, that the value of "x" is required — ideally it will have been calculated in the background and be ready to use; if it is not yet, the code will block at this point and wait, but not sooner.
The compiler takes care of all the gory details to make this work, keeping the code clean and intuitive, so that for the calculation of "x+y" the developer does not need to worry about the state of "x".
The await keyword, new in Oxygene 5.2, takes the use of future types and other asynchronous tasks to the next level, by letting developers write sequential code that will automatically be unwrapped to run asynchronously:
method MyForm.ButtonClick(…)
begin
var x := async CalculateSomethingSlow(); // this is a future
MessageBox.Show("the result is "+(await x));
end;
What happens here is that the use of the "await" keyword when referencing the future type "x" will automatically move the entire statement (and any following code) to run asynchronously. ButtonClick will return right after the future is initialized (making the application stay responsive). The rest of the code is attached to be executed once "x" has been completely evaluated, causing the call to MessageBox.Show() at a later time.
"for" loops can be executed in parallel with the addition of a simple keyword, and the compiler will automatically generate efficient code that spreads the loop over as many threads or CPU cores as makes sense on the system running the application — without creating an excess of threads.
for parallel i := 0 to 1000 do
Console.WriteLine(i);
Of course the execution order of a parallel "for" loop will be undetermined, and code inside the loop cannot safely depend on "previous" iterations of the loop having finished before the current one. The parallel "for" loop is blocking, in that execution does not pass on beyond the loop itself until all iterations have been completed.
We have already seen the use of the "async" keyword above, when defining async futures. But it can also be applied to regular statements and even whole blocks:
begin
…
async begin
DoThis();
DoThat();
end;
DoSomethignElse();
…
Here, the entire "begin"/"end" block with the calls to DoThis and DoThat will be executed asynchronously, while the current thread continues on to call DoSomethingElse.
If the block were assigned to a variable, it would result in a typeless "future" that could be waited for, such as:
begin
…
var x: async begin
DoThis();
DoThat();
end;
DoSomethignElse();
x(); // waits for DoThis/DoThat to be done.
DoMore();
…
Of course Oxygene also provides language primitives for advanced thread synchronization. Whole methods can be appended with the "locked" or "locked on X" directive to enforce synchronized access, and inside of method bodies the "locking" statement can be used similarly, to synchronize individual statements or blocks of statements.
The previous sections focused on the "big picture" features of Oxygene, the language basics, and major features that we classified into modern, special and awesome.
But there are also a lot of little things that make Oxygene great — small language enhancements that may not seem like much, but really change how you write code.
Let's have a look at some of those.
In Oxygene, like in many of the languages it was influenced by, the "." operator is used to call members on a class or object, such as
var x := y.SomeProperty;
This "dereferences" the object contained in "y", calls (in this case) the property getter and returns its value. If "y" happens to be unassigned (i.e. "nil"), an exception is thrown.
The ":" operator works in much the same way, but instead of throwing an exception on an unassigned object, the result will simply be nil. For developers coming from Objective-C, this will be familiar, as that is how Objective-C method calls using the [] syntax work, too. (In Cocoa, the ":" operator will be the equivalent to the usual Objective-C method calls, when porting code).
This works even for simple types, automatically converting the result to a nullable. For example, given:
var x := someString:Length;
the variable "x" will be of type "nullable Int32" and it will contain nil, if someString itself is nil.
Where ":" really shines is when accessing properties in a chain, where any element might be nil. For example, the following code:
var y := MyForm:OkButton:Caption:Length;
will run without error, and return nil if any of the objects in the chain are nil — the form, the button or its caption.
Oxygene provides two nice extensions when comparing values via the equal/greater/lesser operators, and combinations thereof:
Double Boolean Comparisons let you easily encode two checks in one, for example to check if x is between 6 and 10:
if 5 < x <= 10 then begin
In addition, Oxygene supports the proper unicode operators =, = and ? in addition to the two-character combinations of <=, >= and <> — making for nicer and more readable code.
"for each" loops are handy to enumerate over a sequence of elements, and not only save the hassle of dealing with the indexer in a regular for loop, but also are more readable, and work on more types (especially non-indexed collections).
But sometimes it is still useful to know the index within the loop — for example to treat the first iteration different, or perform different output for even vs. odd elements. For this, Oxygene for loops provide an optional index syntax, where
for each s in myStringList index i do begin
…
end;
will automatically give you access to an Int32 variable "i" that keeps count as your loop progresses.
Delphi introduced the "is" and "in" operators for checking class ancestry and membership of a set, respectively. These are great and useful, but awkward to use when checking for a negatory result:
if not (X is String) then …
if not (5 in [1,2,3]) then …
Oxygene alleviates this and makes these checks easier to write and read, by providing explicit "is not" and "not in" operators, making for a more natural and readable flow of code:
if X is not String then …
if 5 not in [1,2,3] then …
Compilers are pretty smart, and can usually figure out of what type a given expression is. So why should you have to repeat yourself and write, say:
var x: List<String> := new List<String>;
when the compiler can do the work for you? Oxygene supports type inference, so in the vast majority of cases, when you are declaring and pre-initializing a variable, you do not need to specify the type:
var x := new List<String>;
and Oxygene will figure it out for you.
Sometimes you want to declare a property that is read-only to the outside world. In most languages, that means marking the property read-only, and using other means throughout your own code to set or change the value. That's ugly, and even worse, error prone, as soon as inheritance comes into play.
Oxygene lets you declare a different visibility for the getter and setter of a property; this way you can — for example — declare a property that can be read publicly, but only written to protected (i.e. by your own class and its descendants).
"implies" is a nifty little binary operator that comes in handy, especially in Class Contracts. It takes two boolean expressions; if the first expression is false, it returns true, but if it is true, it evaluates the second expression instead.
That seems awkward when explained like that, but consider the following example:
ensure
result implies aOutValue > 0;
What this means, essentially, is that if the method result is true, we want to ensure that aOutValue is greater than 0. But if the result is false, then we simply don't care about the second expression.
This kind of logic would be terribly awkward to express with regular and/or operators.
Everyone is familiar with regular "if" statements — a condition is checked, and one or the other statement is executed — and "for" loops.
As of Oxygene 4, "if", "case" and "for" can also be used as expressions — that is, as statements that have a value and can be used inside other expressions:
Console.WriteLine(if result then 'yes' else 'no');
uses an "if" expression of type String to print out "yes" or "no" depending on the value of the boolean "result" (these kinds of expressions come in extremely handy in ASP.NET). The same applies to "case" statements.
"for" loop expressions actually return an "inline iterator", so you could write something along the lines of
var numbers := for x := 0 to 1000 do yield x*5;
and "numbers" would now be a "sequence of Int32" that can contain the numbers between 0 and 5000, in steps of 5.
If you are often dealing with large constant numbers in your code, Oxygene 5.2's new number literals will come in handy, as you can use spacing between groups of digits to keep the number readable — usually in groups of 3 for decimal, and groups of four for hexadecimal (but you can group them anyway you please):
c = 299 792 458;
max32 = $ffff ffff;