module Interfaces {
// DEFINING INTERFACES
// use interface to define code abstractions
// an interface is a set of functions which may be implemented
// for different types and then used polymorphically
// define Point structure used in examples below
struct Point {
x : double;
y : double;
}
// the following defines a Graphics interface, used below
interface Graphics {
move_to(point : Point);
line_to(point : Point);
circle(point : Point, radius: double);
}
// the Drawable interface is invoked to allow a type to draw to a Graphics
interface Drawable {
draw(graphics: Graphics);
}
// interfaces can include other interfaces using ...
// interfaces can have property set/get and index set/get
interface Shape {
...Drawable; // Shape interface includes methods from Drawable interface
origin.get() : Point; // get method for property origin of type Point
}
// IMPLEMENTING INTERFACES
// define Circle structure which will implement Shape interface
struct Circle {
origin: Point;
radius: double;
}
// define Line structure which will implement Shape interface
struct Line {
p1: Point;
p2: Point;
}
// use impl to provide implementions of one or more methods
// of an interface for one or more types.
//
// the first parameter is the type the implementation is for.
//
// this parameter is omitted from the interface definition
// but provided in the impl definition.
//
// using this for the name of the first parameter is
// just a convention - there is nothing special about the name.
//
// specify the interface name with impl, followed by implementations
// for methods of specific types.
// implement methods of Shape interface
impl Shape {
// implement Shape origin get for Line
origin.get(this : Line) : Point => {
// compute origin based on line points
return Point(
(this.p1.x + this.p2.x) / 2,
(this.p1.y + this.p2.y) / 2
);
}
// implement Shape draw for Line
draw(this: Line, graphics: Graphics) {
graphics.move_to(this.p1);
graphics.line_to(this.p2);
}
// implement Shape origin get for Circle
origin.get(this : Circle) : Point => {
return this.origin;
}
// implement Shape draw for Circle
draw(this: Circle, graphics: Graphics) {
graphics.circle(this.origin, this.radius);
}
}
// impl can also be used to define a single method by omitting the {}
// implement Shape origin get for Point
impl Shape origin.get(this : Point) : Point => {
return this;
}
// USING INTERFACES
// to use an interface, cast the defining type to the interface type using ::.
//
// it is not possible to cast from an interface back to the defining type, e.g.
// the implementing type is "hidden" from the user of the interface
fn test() {
let graphics = getGraphics();
let line = Line(Point(1, 2), Point(3, 4));
let shape = line::Shape; // get Shape interface for Line
shape.draw(graphics); // invoke draw method on Shape interface
let line2 = shape::Line; // not allowed: can't cast interface back to Line
}
// MUTABLE INTERFACES
// interfaces are immutable by default, meaning the types that can implement
// the interface must be immutable. use mut to allow mutable types
// to implement the interface.
// define interface for accessing array of data
mut interface DataArray {
get(offset : uint) : u8;
set(offset : uint, data : u8);
}
// type that will implement DataArray in memory
mut struct MemoryArray {
bytes : mut u8[]; // mutable array of bytes
}
// implement DataArray interface for MemoryArray type
impl DataArray {
set(this : MemoryArray, offset: uint, data : u8) {
this.bytes[offset] = data;
}
get(this : MemoryArray, offset: uint) : u8 => {
return this.bytes[offset];
}
}
fn test2() {
// initialize mutable array with 1024 zero bytes
let mem = MemoryArray([0::u8 ## 1024]);
let data = mem::DataArray;
let b = data.get(0); // b is 0
data.set(0, 123);
let c = data.get(0); // c is 123
}
// IMPLEMENTING METHODS ON A TYPE AND AN INTERFACE
// It may be desireable to have methods on a type and have those same methods
// available as an interface. First implement the methods on the type
// and then use impl to signify that the type implements the interface.
// generic Stack interface with push and pop methods
mut interface Stack{T} {
push(value : T);
pop() : T;
}
// type that will implement Stack{int}
mut struct IntStack {
values : mut int[]; // mutable array of int
}
// implement push method for IntStack
push(this : IntStack, value : int) => {
this.values.insert_at(0, value);
}
// implement pop method for IntStack
pop(this : IntStack) : int => {
let value = this.values[0];
this.values.delete_at(0);
return value;
}
// specify that IntStack type implements Stack{int}
impl Stack{int} {
...IntStack;
}
fn test3() => {
// use IntStack directly
let int_stack = IntStack(); // construct IntStack
int_stack.push(1); // direct call on type
// use IntStack via Stack{int} interface
let stack = int_stack::Stack{int}; // cast to Stack{int} interface
stack.push(1); // call through interface
}
}