Tuples and object types

A variable of a tuple or object type is a heterogeneous storage container. A tuple or object defines various named fields of a type. A tuple also defines a lexicographic order of the fields. Tuples are meant to be heterogeneous storage types with few abstractions. The () syntax can be used to construct tuples. The order of the fields in the constructor must match the order of the tuple’s definition. Different tuple-types are equivalent if they specify the same fields of the same type in the same order. The names of the fields also have to be identical.

The assignment operator for tuples copies each component. The default assignment operator for objects copies each component. Overloading of the assignment operator is described here.

  1. type
  2. Person = tuple[name: string, age: int] # type representing a person:
  3. # a person consists of a name
  4. # and an age
  5. var
  6. person: Person
  7. person = (name: "Peter", age: 30)
  8. echo person.name
  9. # the same, but less readable:
  10. person = ("Peter", 30)
  11. echo person[0]

A tuple with one unnamed field can be constructed with the parentheses and a trailing comma:

  1. proc echoUnaryTuple(a: (int,)) =
  2. echo a[0]
  3. echoUnaryTuple (1,)

In fact, a trailing comma is allowed for every tuple construction.

The implementation aligns the fields for best access performance. The alignment is compatible with the way the C compiler does it.

For consistency with object declarations, tuples in a type section can also be defined with indentation instead of []:

  1. type
  2. Person = tuple # type representing a person
  3. name: string # a person consists of a name
  4. age: Natural # and an age

Objects provide many features that tuples do not. Object provide inheritance and the ability to hide fields from other modules. Objects with inheritance enabled have information about their type at runtime, so that the of operator can be used to determine the object’s type. The of operator is similar to the instanceof operator in Java.

  1. type
  2. Person = object of RootObj
  3. name*: string # the * means that `name` is accessible from other modules
  4. age: int # no * means that the field is hidden
  5. Student = ref object of Person # a student is a person
  6. id: int # with an id field
  7. var
  8. student: Student
  9. person: Person
  10. assert(student of Student) # is true
  11. assert(student of Person) # also true

Object fields that should be visible from outside the defining module, have to be marked by *. In contrast to tuples, different object types are never equivalent, they are nominal types whereas tuples are structural. Objects that have no ancestor are implicitly final and thus have no hidden type information. One can use the inheritable pragma to introduce new object roots apart from system.RootObj.

  1. type
  2. Person = object # example of a final object
  3. name*: string
  4. age: int
  5. Student = ref object of Person # Error: inheritance only works with non-final objects
  6. id: int