Interface Extensions

In the following example, the Array type does not implement the PrintSizeable interface. However, we can extend Array to add a member function printSize and implement PrintSizeable.

interface PrintSizeable {
    func printSize(): Unit
}

extend<T> Array<T> <: PrintSizeable {
    public func printSize() {
        println("The size is ${this.size}")
    }
}

Once an extension has been used to implement PrintSizeable for Array, within the current package it looks as if the interface was implemented at the time of the Array type definition.

Therefore, we can now use Array as an implementation type of PrintSizeable, as shown in the following code:

main() {
    let a: PrintSizeable = Array<Int64>()
    a.printSize() // 0
}

The result is as follows:

The size is 0

We can implement multiple interfaces within the same extension, separating them with &. The order of the interfaces does not matter.

As shown in the following code, we can implement I1, I2, and I3 for Foo in one extension:

interface I1 {
    func f1(): Unit
}

interface I2 {
    func f2(): Unit
}

interface I3 {
    func f3(): Unit
}

class Foo {}

extend Foo <: I1 & I2 & I3 {
    public func f1(): Unit {}
    public func f2(): Unit {}
    public func f3(): Unit {}
}

We can also declare additional generic constraints in the interface extension to satisfy specific interface requirements.

For example, we can make the Pair type implement the Eq interface, allowing Pair itself to conform to the Eq constraint, as shown in the following code:

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> <: Eq<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

If the type being extended already contains some or all of the functions or properties required for it to implement the given interface, we do not need to re-implement those functions or properties in the extension. (More precisely, we cannot do that, because extensions can never hide, override or redefine the existing members.)

In the following example, we define a new interface Sizeable, implementing which enables obtaining the size of the implementing type instance via its member function size. Since we know that the type Array already contains such function, we can extend Array to implement Sizeable without actually adding a size function to the extension:

interface Sizeable {
    prop size: Int64
}

extend<T> Array<T> <: Sizeable {}

main() {
    let a: Sizeable = Array<Int64>()
    println(a.size)
}

The result is as follows:

0

If the interfaces implemented by multiple interface extensions have inheritance relationships, the extension implementing the interface will be checked prior to the extension implementing the subinterface.

For example, if interface I1 has a subinterface I2, I1 contains a default implementation, and the interface and subinterface are implemented by two extensions of class A, the extension implementing I1 will be checked first, followed by the extension implementing I2.

interface I1 {
    func foo(): Unit { println("I1 foo") }
}
interface I2 <: I1 {
    func foo(): Unit { println("I2 foo") }
}

class A {}

extend A <: I1 {} // first check
extend A <: I2 {} // second check

main() {
    A().foo()
}

Compile and execute the preceding code. The output is as follows:

I2 foo

In the preceding example, when the extension implementing I1 is checked, the foo function is inherited from I1. When the extension implementing I2 is checked, this inherited foo function will override the existing foo function (which is also the default implementation) in A with the same signature. Therefore, when the foo function in A is called, it finally points to the implementation in I2 (subinterface).

If inheritance conflicts occur between the interfaces implemented by two interface extensions of the same type, the check sequence cannot be determined. As a result, an error will be reported.

interface I1 {}
interface I2 <: I1 {}
interface I3 {}
interface I4 <: I3 {}

class A {}
extend A <: I1 & I4 {} // error: unable to decide which extension happens first
extend A <: I2 & I3 {} // error: unable to decide which extension happens first

If inheritance does not exist between the interfaces implemented by two interface extensions of the same type, the extensions will be checked at the same time.

interface I1 {
    func foo() {}
}
interface I2 {
    func foo() {}
}

class A {}
extend A <: I1 {} // Error, multiple default implementations, need to re-implement 'foo' in 'A'
extend A <: I2 {} // Error, multiple default implementations, need to re-implement 'foo' in 'A'

NOTICE

When the foo function is called through an instance of class A, unexpected behavior will occur if the following conditions are met: class A has a generic base class B<T1, ...,Tn>; B<T1, ...,Tn> extends interface I<R1, ...,Rn>, which has a default implementation instance or static function (such as foo); the function is not rewritten in B<T1, ...,Tn> or its extension; and class A does not directly implement interface I<R1, ...,Rn>. This issue will be resolved in later versions.

interface I<N> {
    func foo(n: N): N {n}
}

open class B<T> {
}

extend<T> B<T> <: I<T> {
}

class A <: B<Int64>{
}

main() {
    A().foo(0) // this call triggers unexpected behaviour
}