Delight

Aug 2009, Thomas Leonard

Delight inherits most of its type system from D. This page contains a brief summary, and a list of the changes from D.

Basic Types

The basic types are the same as in D. The main ones are:

# Integers, signed and unsigned
bool
byte, ubyte
short, ushort
int, uint
long, ulong

# Floating point values with various levels of precision
float, double, real

# Complex values
cfloat, cdouble, creal

# UTF 8/16/32 elements
char, wchar, dchar

Arrays

There are three types of array:

# A static array
# (four integers, initially all zero)
int[4] a
a[0] = 50

# A dynamic array
# (initially empty)
int[] b
b ~= [1,2,3]    # Append three values

# An associative array (hash / dictionary / map)
# (initially empty)
int[int] c
c[400] = 123

Array accesses are bounds-checked.

An important difference between D and Delight is that a Delight array is considered true if and only if its length is non-zero:

int[] a
assert not a # Empty

a = []
assert not a # Still empty

a = [0]
assert a     # Contains a 0

In D, an empty array may be considered true or false, depending on exactly how it was allocated. Also, D allows null to be used to mean an empty array. In Delight, you can only use [].

Const and Invariant

Delight inherits D's powerful (but somewhat complicated) const/invariant system. Some examples:

# This function requires an array it can change
void a(char[] data): ...

# This function will not change the array
void b(const(char)[] data): ...

# This function requires an array that will not change
void c(invariant(char)[] data): ...

Neither b not c can change their input array, but c additionally requires that no-one else will change it either. For example, c might want to store the data somewhere and use it later.

These modifiers are transitive: if foo is const then so are foo.a and foo.a.b, etc.

Mutable and invariant types are implicitly converted to const where needed.

If an object is const, only const methods on it can be called. A const method looks like this (days):

class Foo:
	int weeks = 2

	int days() const:
		return weeks * 7

	unittest:
		const f = new Foo()
		assert f.days == 14

This has the effect of making the implicit this parameter const in the method.

Strings

The string type is defined as an alias for an array of invariant characters:

alias invariant(char)[] string

Therefore, strings never change. If you want to edit a string, use dup to make a mutable copy. Use idup to get an invariant copy:

string spaceToUnder(string before):
	char[] buffer = before.dup
	for ref char c in buffer:
		if c == ' ': c = '_'
	return buffer.idup

assert spaceToUnder("a nice message") == "a_nice_message"

(note the use of ref to make c refer to the element in the buffer, rather than a copy of it)

Classes and Structs

Structs work just as in C. They are allocated on the stack and passed by value (copied).

Classes are implemented as pointers. They are allocated on the heap and passed by reference.

class Foo:
	char[100] name

struct Bar:
	char[100] name

void myFunc():
	Foo foo = new Foo()
	Bar bar

Class pointers cannot be null by default. See the NullPointerException page for details.

You can also use pointers (as in C), but usually you won't need them. Arrays are bounds-checked, but there are no safe-guards when using pointers.

Functions

If you want a variable to hold a call-back, use:

int myFunc(string a):
	...

int delegate(string a) myCallback
myCallback = &myFunc

myCallback("foo")

A delegate is a structure containing two pointers: the address of the function and a context. A delegate can therefore be used to hold a method on a particular object. There is also a simpler function type which only holds one address. You should probably avoid using this as it's less flexible.

Enums

# A named enum
# (creates a new type extending int)
enum Colour: Red, Green, Blue

assert Colour.Red == 0
assert Colour.Green == 1
Colour c = Colour.Blue

# An anonymous enum
enum extends bool: On = true, Off = false

assert On == true

# The values can be specified explicitly
enum Flags:
	None     = 0
	Needed   = 0x1
	Provided = 0x2
	Error    = 0x4

Casting

Casting a class to another class results in null if the object isn't derived from the target class or interface. The result is therefore a maybe type, which you can remove using an if statement, e.g.

void age(Tree tree):
	if Oak oak = cast(Oak) tree:
		oak.produceAcorns()

Of course, casting should be avoided where possible. A generic produceSeed method would have been more appropriate in this case.

Next Step

Install it and try it out...