SourceForge.net Logo

One syntax to rule them all

Last modified on 2008/03/09



Beta version, implemented in C++ only


Most mainstream programming languages are based on type systems, which in their overall shapes are very similar. For example, type system of the C language contains array types, structure types and unions. Type system of Harpoon is similar, although it serves a bit different purpose.

There are actually two main reasons for types in Harpoon. First is verification - types are intended to describe structure of data expected at some point of processing. For example, a search engine of some internet bookshop may expect information about offered books to be in a form of records with some specified fields. This can be declared as:

typedef Book:

record {

title : String;

authors : list(String);

price : Price;

pages : Integer;

available : Bool : true; //default value


The second purpose of types is to resolve abbreviations. It is possible, for example, to write a tuple where a record is expected and later automatically covert the expression according to some type declarations. This feature allows Harpoon to be both a simple language (nearly as Lisp) and to allow concise, human-friendly expressions. For example, the following automatic conversion uses tuple to record transformation, tag completion and default value completion:

Abbreviation (for humans):

( "Steve" 24 )

Resolved (for computer):



name = "Steve"

age = 24

alive = true


One of the most interesting facts about type declarations in Harpoon is that they are written in the Harpoon itself. If you think this is obvious, see the XML Schema.


There are three groups of types:

  • primitive - Int, Real, String and Ident as already described in language,

  • structural - record, tuple and list types,

  • denotational (or set-theoretic) - unions and enumerations.

A type consists of a tag, a structure and its default value, (and constraints in future).

The tag is the type name, but it is also a part of the type - types differing only in tags are considered different (as classes in C++/C#). And there can not be two types with the same tag, because tags also identify types globally. Besides, there are anonymous types - they have their tags empty and can be defined locally.

Record types

A record type defines the list of field names and types. It is expressed with a list of field declarations, each of which is a tuple containing the field name, the type and optionally of the default value.

Record types allow the following automatic transformations (resolving abbreviations):

  • Conversion from a tuple to a record (adding the fields' names),

  • Completion of missing fields with the default values,

  • Reordering of the fields,

  • Completion of the tag.

Tuple types

A tuple type defines separately type of each element of a tuple. It is expressed with a list of type declarations corresponding to consecutive elements.

For example:

typedef StringAndInt :

tuple { String, Int }

For abbreviations resolving, tuple types allow:

  • Completion of missing elements at the ends (with their default values),

  • Completion of unknown elements (the underscore) in a middle (with their default values),

List types

A list type defines the type of all elements.

For example:

typedef ListOfInts : list( Int )

typedef ListOfPairs : list( tuple {Int, Int} )

Enumeration types

An enumeration type defines a finite set of the elements, which are enumerated.

For example:

typedef ScreenColors : enum { RED, GREEN, BLUE }

Typically the enumerations contain identifiers, but they are not limited to. They are allowed to contain any data objects. This is very different to the enumerations in C, which actually do not enumerate anything, but define new global constants.

Union types

An union type (aka variant) defines a supertype of several types enumerated in its definition. It is useful for variables that can have values of two or more types.

For example:

typedef Book :

record {

authors : union { String, list(String) }



typealias Statement :

union {






There is also a special union type Any, which is an infinite union of all possible data.

Strong type definitions vs type aliases

There are two constructs for defining named types - typedef and typealias. The former defines a new named type (with a tag), whereas the latter defines a new name for an existing named or anonymous type.

For example:

// Creating new type

typedef Point : record { x : Int; y : Int; }

// New name for an exiting named type

typealias ObjectLocation : Point;

// New name for an exiting anonymous type

typealias Authors : union { String, list(String) }