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 them 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.
The language aims at being ~99% identical and compatible between the platforms, but there are a few subtle differences owed to the underlying platforms:
- Platform Differences Overview
- Language Topics for .NET
- Language Topics for Cocoa
- Language Topics for Java
On the Cocoa platform, allowances are made for accessing non-object-oriented platform APIs, like the C runtime functions.
Object Pascal, just like Pascal before it, is easily readable by both
humans and computers. This is by design, and is accomplished through the
use of English words instead of symbols to define the language. For
example, blocks of code are delimited with the
keywords. Compare this to the C family of languages that use the curly
} to accomplish the same task.
This extends through all aspects of the language, aiming for a "read as plain english" approach for features, where feasible. For example, where C# and Swift use a cryptic
? to mark nullability, Oxygene uses the
nullable keyword, and so on.
Case & Naming Conventions
By convention, reserved words in Pascal were originally all in uppercase. The language itself, however, is not case sensitive. With the advent of syntax highlighting, IDE’s reserved words are now typically lowercase by convention.
In older Pascal dialects, like Turbo Pascal and Delphi,
it was convention to prefix class names with a
T; this is no longer
the case with Oxygene (although Oxygene does preserve the convention of
prefixing interface types with
I to distinguish them from regular
Aside from that, there are no formal naming or case conventions in Oxygene. Typically, the naming and case follows the conventions of the underlying framework – which is a mixture of "PascalCase" and "camelCase", depending on the platform. For cross-platform or non-platform-specific code, "PascalCase" is recommended.
One of the things that differentiated Pascal when it was introduced is
that it was designed to be a structured programming language. This
translates into two things: The first is that it was designed to support
complex data structures like lists and records. The second is that it
supports various control structures like
repeat, etc., as well as procedures & functions, without the need to
jump like unstructured languages (in fact, Oxygene
goto keyword that is available in legacy Pascal altogether).
Code File Structure
All the code in an Oxygene project is divided into code files. A code
file can contain a single class (often recommended) or more than one
class or auxiliary type. By defaut, all types defined in a code file are
members of the same namespace; that namespace
is specified at the top of the file via the
namespace replaces the unit of classical Pascal, but the latter is supported for backward compatibility.
Multiple files, or even multiple projects, can of course contribute to the same namespace — in fact, for small to medium projects, it will be common for all types to be situated in the project's one and only namespace.
Namespaces provide a way to group related classes and can also prevent
naming collisions for different classes that have the same name. For
example, if there are two Button classes, one can be in the Foo
namespace, while the other is in the Bar namespace. Any code in the Foo
namespace will reference the Foo.Button class by default when just
referring to it as
Button. If that code needs to refer to the other
Button class, it can refer to it using what is called the "fully
namespace declaration, each code file is divided into two
interface and the
implementation section. The interface
section is similar to the separate header file found in many languages
from the C family. It defines the public interface of the code found in
that code file.
The implementation section is the details of the code file. This is the location of the code that implements the classes defined in the interface section. A class could be defined in the implementation section alone, but it would not be accessible from other code files. The implementation section provides a level of encapsulation of the complexity of the implemented code.
The advantage of this separation is that it provides a convenient human- and computer-readable summary of the APIs in the code file. This speeds up human navigation and comprehension of the types when consuming the classes elsewhere.
Each section may include a
uses clause that can bring additional
namespaces "into scope". Once a namespace is listed in the
clause, the types it contains can be referred to without needing the
fully qualified name.
namespace LutherCorp.WorldDomination; interface uses LutherCorp.DominationTools; type WorldDominator = class public method AchieveWorldDomination; end; implementation method WorldDominator.AchieveWorldDomination; begin // Do something end; end.
The end of every code file is indicated with the end keyword followed by a period. Everything beyond that point will be ignored.
Classes defined in Oxygene belong to a class hierarchy rooted in a base
Object (which maps to
System.Object in .NET,
java.Object in Java and
Foundation.NSObject in Cocoa). Each class
declaration either explicitly specifies an ancestor class, or will
implicitly descend from
Classes inherit all members from their ancestor classes and are thus said to extend that class – commonly by adding additional members or providing overridden implementations of select members defined in the ancestors.
Classes can be marked as
abstract to indicate that they need to be
extended in order to be instantiated and used. They can also be marked
sealed to indicate that they cannot be extended further.
Methods and Other Class Members
Methods define the execution flow of applications written in Oxygene. Each method consists of a list of Statements that get executed in turn when a method is called. Statement types include loops, conditional statements, logical and mathematical expressions or calculations, or calls to other members in the same or different classes.
Pascal originally differentiated between procedures and functions (the
latter being a procedure that returned a result) by using different
identifiers, and early Object Pascal implementations carried this legacy
over when introducing object-orientation, although this terminology no
longer seemed accurate. The Oxygene language no longer differentiates
the two by keyword and consistently uses the
method keyword for, well,
methods (although the
function keywords remain
available as compatibility option).
Properties provide controlled access to data contained within a class instance, and can be read/write, read-only or (in rare cases) write-only. Events allow objects to retain references to call-back methods on other objects that they can call, when necessary.
While methods and properties (next to private fields or instance variables) are the two core kinds of members that make up classes, other more advanced member kinds are supported as well, such as Events, Invariants or .
Class members can be declared at different visibility
levels to control how they
can be accessed from outside the class they are in. The three most
important levels include
private visibility, which restricts access to
the class itself;
protected visibility, which allows access only from
descendants of the class; and
public, which allows any code in the
project to access the member (but several more fine-grained visibility
levels are available as well).
With Oxygene being a truly object oriented language, all code you write will generally be contained in classes (or encoded in other types), with no "global" functions or other elements.
Classes are the core of any object oriented program, but they are complemented by a range of other kinds of types that enrich the language:
- Interfaces are abstract types that define a set of shared methods, events or properties that can be implemented by one or more (otherwise unrelated) classes. Classes that implement the same interface can be accessed in identical ways by code that might otherwise be unaware of the concrete class types. This makes it easy to write code that can act on "similar" classes, without those classes having to share a common ancestor. Many developers consider Interfaces to be a cleaner replacement for multiple inheritance, which Oxygene intentionally does not support.
- Records behave similar to classes, but are . They can also contain fields, methods and properties, like classes do.
- are simple types that provide a collection of named values.
- Standard Types include Simple Types such as integers, floats and strings, as well as more complex types such as arrays, sets and .
The Oxygene language contains numerous advanced language concepts, many of which are common to most modern languages, some of which are inspired by other less mainstream languages and yet others which are entirely unique to Oxygene.
- Class Contracts allow optional code to be included with class definitions and method implementations to enforce class consistency. These contracts can be enforced at runtime, leading to more precise and timely error reporting when constraints are not met as expected.
- Sequences and are deeply integrated into the language to work with various types of lists of objects, iterate them, and perform advanced queries (such as filtering, sorting or combining) on them.
- Several language constructs for Future Types, Asynchronous Expressions and more. are integrated into the language to make it easy to write multi-threaded code that scales well from single-CPU to multi-core computers. These include ,
- provide for classes to be customizable to work with different types without having to write separate implementations. For example, a generic list class can be implemented (or indeed provided by the underlying frameworks) and then be instantiated to be a list of a very specific concrete type.
With very few minor exceptions dictated by the underlying runtimes, the Oxygene language is designed to be virtually the same across all three supported development environments: .NET, Cocoa and Java.
This means that the language syntax is 99% identical on all three platforms, and that a lot of the code you write can be shared between the platforms (as long as it does not use platform-specific APIs, of course), and that all the language knowledge and experience you build up using Oxygene can be applied to all platforms. Using the same language also makes it easier and more intuitive to work on apps for the different platforms, without having to "switch gears" between – say – C#, Java and Objective-C all the time.
And the open source Sugar base library makes sharing code between projects across all three runtimes even easier.