Note: Heron2C does not yet implement interfaces
Heron interfaces follow a novel approach, I first described for C++ in the article Interfaces in C++, in the September, 2004 issue of the C++ Users Journal. I have recently described the technique for C++ in two articles at CodeProject.com, http://www.codeproject.com/cpp/retrofitpolymorphism.asp and http://www.codeproject.com/cpp/retrofitpolymorphism2.asp
The approach I described for C++ is also currently being used to create the Boost Interfaces Library ( BIL ) by Jonathan Turkanis ( see the Boost wiki for more information the BIL ). The BIL semantics are very similar to those of Heron interfaces, but are achieved through macros, and some unfortunate syntax due to C++ limitations.
Even though interfaces are not yet implemented in the Heron2C compiler, they are a central concept to the design of the Heron language. Interfaces are a striaghtforward concept used to replace numerous other features as explained in more detail below.
Heron interfaces are very different from interfaces as implemented in other programming languages. The most prominent difference is that they do not require declarations within a class of intention to implement an interface. In other words interface implementations are implicit. Consider the following example:
types {
class Dog {
MakeSound() : String {
return "woof";
}
GetNumLegs() : Int {
return 4;
}
GoFetch() {
// ...
}
}
class Duck {
GetNumLegs() : Int {
return 2;
}
MakeSound() : String {
return "quack";
}
FlySouth() {
// ...
}
}
interface IAnimal {
MakeSound() : String;
GetNumOfLegs() : Int;
}
}
functions {
_main() {
IAnimal& animal;
Dog dog;
Duck duck;
animal = @dog;
P(animal.MakeSound()); // outputs woof
animal = @duck;
P(animal.MakeSound()); // outputs quack
}
}
Notice that the Dog and Duck classes do not need to know anything about the existance of IAnimal.
Because polymorphism is provided externally to a class, it means that any class can be used polymorphically and also that there is no overhead of vtable pointers within a class. Most OOP languages, if not all, require a significant overhead to provide polymorphic behaviour.
The only caveat is that the interface references are typically twice the width of an an ordinary reference ( technically this is not neccessarily the case, as I will explain below ). On the other hand the overhead of multiple interface implementation in other languages is considerably more significant.
Another advantage of the Heron interfaces approach, is that you don't need to incur any of the cost of run-time polymorphism, until when and if you explicitly require it. That cost includes the inability to inline functions, the performance hit of dynamic dispatch and the space overhead of function dispatch tables.
An interface supplants the need for virtual functions, all dynamic dispatch is done through interface references. This means that intra-method calls can not be dispatched dynamically unless a derived class passes to its inherited parent an interface reference. This is intentional in part because virtual functions can lead to complex code, and violate object oriented principles such as information/implementation hiding. Virtual functions often lead to situiations where the objects are not tested properly for all possible inheritance scenarios.
Because the interfaces approach to polymorphism doesn't need virtual functions, calls from one method to another can be inlined. Intra-method calls to virtual functions can not usually be inlined, because they are potentially dispatched at run-time. This is illustrated within the C++ Object Oriented Template Library, specifically within the file dispatch-timings-test.hpp.
Function pointers can be effectively implemented as intefaces with only one function. This helps simplify the language significantly when compared to C++ for instance.
Parameterized functions, as found in C++, have two advantages, 1) they are generic, 2) they are efficient. The big disadvantage, is that it can be hard to restrict the kinds of types used, since C++ lacks a mechanism for concept checking. Heron interfaces are sufficiently generic, and provide a form of static type checking. In manuy conditions the type of the value passed to an interface function parameter is known at compile time. If this is the case and the function does not implicitly require the parameter to in fact be an interface, the compiler can provide an optimization that replaces the interface with the concrete type. This entirely removes the overhead of dynamic dispatch, and allows function inlining.
To illustrate the optimization consider the following code, using the previous IAnimal interface:
HasTwoLegs(IAnimal i) : Bool {
return i.GetNumLegs() == 2;
}
_main() {
Dog dog;
if (HasTwoLegs(dog)) {
P("Two legged animal");
}
else {
P("Four legged animal");
}
}
Clearly in this example the compiler can easily figure out that HasTwoLegs() is called with a value of type Dog, and can replace instances of IAnimal with the concrete type Dog. The optimization effectively rewrites the code then as follows:
optimized_HasTwoLegs(Dog i) : Bool {
return i.GetNumLegs() == 2;
}
_main() {
Dog dog;
if (optimized_HasTwoLegs(dog)) {
P("Two legged animal");
}
else {
P("Four legged animal");
}
}
This can now be more fully optimized using function inlining and other techniques. The application of this optimization is up to the language implementation.