Variants in Clump

In this document we demonstrate how objects and variants can be used in Clump. It covers the syntax and the expressiveness of this concept in conjunction with classes.

In Clump end-user can specify objects. Such object is usually a set of attributes. While attribute can be a function records "a la Cardelli" are therefore supported in the language.

final object Nil {}

final object Cons<E> {
    final E head;
    final List<E> tail;

    Cons(E head, List<E> tail) {
        this.head = head;
	this.tail = tail;
    }
}
In this example the tail attribute is specified as a List and a list can be Nil or Cons. This can be specified by a variants as illustrated in the next example.
type List<E> = Nil | Cons<E>

In addition a typecase statement can be used when objects must be manipulated or decomposed. Such operation is limited to types and does not offers any mechanism like pattern matching. Only topmost type object is checked. In addition when the object verifies the typecase it can be linked to a variable which has the selected type.

class main(System) implements Main {
    List<String> toList(int index, String[] args) {
        if (args.size() == index) {
	    return new Nil();
	} else {
	    return new Cons<String>(args[index], toList(index+1, args));
	} 
    }

    main(args) {
        List<String> list = toList(0, args); 
        
        switch(list) {
        case Nil as nil: 
             this.stdout().println("no argument available ...");
        case Cons<String> as cons:
             this.stdout().println("At least one argument is available");
        }
    }
}

As Clump propose a separation between class and objects such type case fas the foundation for the design of classes. A Class provides a set of behaviors to a given type and then if such type is a variant each method can be design for each object separately or not. This is illustrated in the next example where head and tail methods are designed for a List.

object EmptyListException {}

interface IList<E> {
    E head() throws EmptyListException;
    List<E> tail() throws EmptyListException;
}

class list<E>(List<E>) implements IList<E> {
    // Behaviors dedicated to the empty list denoted by Nil
    case Nil { 
        head() { throw new EmptyListException(); }
        tail() { throw new EmptyListException(); }
    }

    // Behaviors dedicated to the list with at least one element
    case Cons<E> {
        head() { return this.head; }
        tail() { return this.tail; }
    }
}

As Clump is a object-oriented programming language such paradigm involves mechanisms like inheritance. In such case the class design can be first done for each object. Finally using the inheritance the implementation for a variant type can be natural.

class nil(Nil) implements IList<E> {
    head() { throw new EmptyListException(); }
    tail() { throw new EmptyListException(); }
}

class cons<E>(Cons<E>) implements IList<E> {
    head() { return this.head; }
    tail() { return this.tail; }
}

class list<E>(List<E>) extends nil, cons<E> implements IList<E> {
    // nothing
}