Direct Extensions

The following example illustrates the syntactic structure of a simple extension:

extend String {
    public func printSize() {
        println("the size is ${this.size}")
    }
}

In the above example, an extension is declared using the extend keyword, followed by the type being extended (String) and a member function printSize in the body enclosed in braces { }.

String gets extended with the printSize member function in the current package only. The function can be accessed for any instance of String in the current package as if it was a member function of String:

main() {
    let a = "123"
    a.printSize() // the size is 3
}

The result is as follows:

the size is 3

When the type being extended is a generic type, there are two syntaxes available for extending its function:

One is to extend a specific instantiated generic type. The keyword extend can be followed by a generic type that is completely instantiated. The added functions can be used only when the types fully match. In addition, the type arguments of all generic types involved must comply with the constraints defined for those generic types, if any.

In the below example, only the types implementing the ToString interface are valid type arguments for Foo<T>:

class Foo<T> where T <: ToString {}

extend Foo<Int64> {} // OK: Int64 implements ToString

class Bar {}
extend Foo<Bar> {} // Error: Bar does not implement ToString

The other syntax introduces a generic extension. It has type parameters enclosed in angle brackets < > after extend. Generic extensions can be used to extend generic types that are not instantiated or partially instantiated. The generic parameter declared after extend must be directly or indirectly used in the extended generic type. The functions added for these types can be used only when the types and constraints fully match.

For example, for MyList<T>:

class MyList<T> {
    public let data: Array<T> = Array<T>()
}

extend<T> MyList<T> {} // OK
extend<R> MyList<R> {} // OK
extend<T, R> MyList<(T, R)> {} // OK
extend MyList {} // Error
extend<T, R> MyList<T> {} // Error
extend<T, R> MyList<T, R> {} // Error

For the extensions of generic types, you can declare additional generic constraints for the additional functionality to be available only for certain (combinations of) type arguments.

For example, we can define a pair type, which can store two elements of any types (similar to a tuple).

To ensure that the pair type indeed can contain elements of any type, its two type parameters should not have any constraints.

However, when each of the two element types supports equality testing with the equals member function, we would naturally like that instantiation of the pair type to do so as well. We can use an extension to implement an equals function for pairs with such element types.

As shown in the following code, the extension syntax is used to constrain that a pair can implement the equals function only when the types of both its elements support equals:

class Pair<T1, T2> {
    var first: T1
    var second: T2
    public init(a: T1, b: T2) {
        first = a
        second = b
    }
}

interface Eq<T> {
    func equals(other: T): Bool
}

extend<T1, T2> Pair<T1, T2> where T1 <: Eq<T1>, T2 <: Eq<T2> {
    public func equals(other: Pair<T1, T2>) {
        first.equals(other.first) && second.equals(other.second)
    }
}

class Foo <: Eq<Foo> {
    public func equals(other: Foo): Bool {
        true
    }
}

main() {
    let a = Pair(Foo(), Foo())
    let b = Pair(Foo(), Foo())
    println(a.equals(b)) // true
}

The result is as follows:

true