Abilities

The ability to re-use code in a number of different contexts is a very desirable property for a language.  It significantly reduces development time and makes programs higher quality, because the code is effectively retested each time it is reused . It is therefore important to be able to make code as re-usable as possible.

If we wish to write a generic algorithm for e.g. sorting,  then we want to be able to any objects which can be compared to each other, and to any data structure which maintains an ordering on some objects.  Java goes some way to allowing this by providing the Comparable interface which allows to objects to be compared and the List interface which maintains an ordered set of objects.  The Comparable  interface is a very small interface with just one method compareTo(Object o) which is very precisely defined in the documentation.  I call such a small interface an ability because it represents the ability to do one particular thing (in this case, the ability to compare the object with another) and that thing is defined in detail.  It follows that abilities are very small interfaces which represent a single facet of functionality - they typically have names ending in-able, but this is not required.  

The Java List interface is not an ability but a rather bloated interface with lots of features like sub-lists, iterators, array conversion, index-of operators etc.  Implementing List fully takes a great deal of effort - usually methods which are rarely used are either left unimplemented or are implemented poorly and left untested.  It makes sense to break List down into a set of abilities, each of which represents some independent functionality of the list.  These could include CanAdd (the ability to add elements), Iterable (the ability to provide an iterator for iterating over contained elements), Container (the ability to check if an element is contained in this object).  The interfaces List and ModifiableList can then be used to represent sets of abilities.  This approach is used in Jura to allow maximum re-use of code.

Note: Some languages use dynamic typing to achieve code re-use.  This is dangerous as, just because a class has a method of a particular signature, doesn't mean that the method does what is expected.  By implementing an ability interface, the class is stating that it not only has a method of the right signature but also that the method fulfills the terms of the contract of that ability. 

Core abilities

They key to effective use of abilities is that a number of generic abilities should be defined in the core classes of a language.  Third party classes should then implement these abilities as appropriate and define new domain-specific abilities.  Ideally, almost no functionality should be defined unless it fulfils an ability.

Jura provides a number of core abilities in the jura.lang.ability package, which include:

Ability name Method Description
Initialisable void initialise() After an instance of a class has been created, initialise() will be called automatically if it implements this ability.
Validatable void validate() throws Exception An class whose state can be validated should implement this ability.  It will be called automatically (following any creation operations, like setting of properties) during creation of an  instance of the class and should throw an exception if the instance has invalid state.  It can also be called at other times when the state of the object need be validated.
Invokable Object invoke(Context c, List arguments) Ability of an object which can be invoked with some fixed number of arguments.
HasName String getName() Ability of an object which has a name, which typically is unique to this object in a particular context.
EntryPoint void main() Ability of an object to act as an entry point for an application.

 

Collection abilities

Jura defines collection classes in the jura.util package. Corresponding abilities are in the jura.util.ability package. Almost all collection abilities use generics to indicate the element type of the collection.  The most basic collection abilities are listed below, property names are listed  in the braces.

Ability name Method Description
Iterable{elementType} Iterator{elementType} iterator() The ability of a collection to allow iteration over that collection using an Iterator object.
Clearable void clear() The ability of a collection to allow all elements to be removed.
CanAdd{elementType} boolean add(elementType obj) The ability to add an object of type Type to the collection.
CanInsert{elementType} void  add(Integer index,elementType obj) The ability to insert an object into an ordered collection.
CanRemove{elementType} boolean  remove(elementType obj) The ability to remove an object of type Type from the collection.
CanGet{keyType,valueType} valueType get(keyType key) The ability to get an object of type Type2 from the collection based on a key of type Type1.
CanPut{keyType,valueType} void put(keyType key,valueType value) The ability to put an object of type Type2 into a collection based on a key of type Type1.
Container{elementType} boolean contains(elementType obj) Allows testing to see if the object contains a particular element.
HasSize int getSize() Allows determining the size of an object.
HasElementType Type getElementType() Ability of a generic collection template for collections which have typed elements.

 

Collection interfaces then implement sets of abilities.

Collection Interface Extends Description
Collection{elementType} HasSize, Container{elementType}, Iterable{elementType} A collection of a known number of elements.
Map{keyType,valueType} Collection{Map.Entry{keyType,valueType}}, CanGet{keyType,valueType} A map with a known number of map entries.
List{elementType} Collection{elementType}, CanGet{Integer,elementType} A collection with a known ordering.
ModifiableCollection{elementType} Collection{elementType}, CanAdd{elementType}, CanRemove{elementType}, Clearable A collection which can be modified.
ModifiableMap{keyType,valueType} ModifiableCollection{Map.Entry{keyType,valueType}}, 
Map{keyType, valueType}, CanGet{keyType,valueType}
A map which can be modified.
ModifiableList{elementType} ModifiableCollection{elementType},List{elementType}, CanPut{Integer,elementType} A list which can be modified.

 

Note that the Map interface is defined to be a collection of map entries (for consistency with the Java Map interface).  However, the CanGet ability is more flexible and can be implemented by collection of other objects, such as List which uses CanGet to provide an ordering based on mapping from integers to objects, but does not itself implement the Map interface.

Note: The prototype abilities and interfaces may not be exactly as stated here.

Using abilities

When writing a general purpose algorithm, it is best to test for abilities using IfCast (or similar) rather than full-size interfaces unless all the abilities on that interface are necessary for the algorithm.

As a simple example, this algorithm will add the supplied string to a container if it is not already in the container.

Method:addNoDuplicates{ returns=Boolean
  Par:c{type=Container{elementType=String}}
  Par:s{type=String}
  //
  If{test=c.contains(s) // Return{false}}
  IfCast{var=c type=CanAdd{elementType=String}  //
    {* c is now cast to CanAdd *}
    c.add(s)
    Else
    {* c does not implement CanAdd{String}, so throw an exception. *}
    Throw{new UnsupportedOperationException{msg="Cannot add strings to "+c}}
  }
  Return{true}
}