Menu Close

Articles

Level: Composite Types

A composite type is created using a group of elements all having same type or  mixed types. We use composite types to create collections of elements. They can have names or can be accessed using an index. The index can be an native type: Number or  a String depending on the collection type.

Implementation

Level dialect implement composite types gradually.

  • Level-1: {Tuple, Record, Vector, Matrix, Option };
  • Level-2: {List, Set, Map, Unicode, Variant};
  • Level-3: {Table, View};

Tuple

A tuple is a finite sequence of elements. In mathematics, an n-tuple is an ordered list of n elements, where n is a non-negative integer. Level language use this concept very frequent to define a composite collection of something. Tuples can be declared with a name or anonymous used in expressions.

Tuple literal

The tuple literal is an enumeration of elements enclosed in round brackets and separated by comma like this: (1,2,’a’,’b’, ‘c’). Tuples are immutable and have fixed length. A tuple can be assign to a variable or can be anonymous. Tuples can be initialized with literals or can be result of a function.

Declaring a tuple variable

<tuple_name>: Tuple of (type,[,type]...);

Using type inference

<tuple_name>=(type,[,type]...);

Empty tuple

An empty tuple is called “unit” and is represented like this “()”. A program call a procedure or function using a tuple to enumerate parameter values. For functions that do not have parameters we use unit () after the name of the function to differentiate between a function reference and a function call.

  -- calling a function with no parameters
  <variable> = <function_name>();

Examples:

let
  -- example of tuple declaration 
  v_example:Tuple of (String, Integer, Integer, String);  
do
  -- example of tuple initialization
  v_example:=('a', 100, 200, "this is the tuple");

  -- following logical expressions are true
  print(v_example[0]); --> 'a'
  print(v_example[1]); --> 100;
  print(v_example[2]); --> 200;
  print(v_example[3]); --> "this is the tuple";
done;

Usage

Tuples are very important language components:

  • We use tuples to represent a list of parameters;
  • We use tuples to create a list of arguments;
  • When a method do not have parameters we use the unit tuple “()”
  • A function can return a tuple as result.
  • We can capture results of a function into multiple results separated by comma.
  • Concatenation operator can use a tuple for the right side of the operation.
  • A tuple can represent an enumeration of expressions in λ-functions.
  • A tuple is internally used to extract (key, value) pairs using “in” operator into a for loop.

Note: A tuple collection is similar to a record except that elements do not have names. Instead the elements of a tuple can be identified by subscript [x]  where x in in range (0 .. n-1).

 


Vector

A vector is a collection of elements having a predefined capacity and element type. Elements are stored in order of subscript or index. All elements must be of the same type. Vector elements can be of native types, pointers or references to other composite variables.

-- general declaration of a vector
<var_name>: Vector(n) of <type>;

A vector has a precise capacity n that must be specified on vector declaration or initialization. You can’t add or remove elements once the vector capacity is established. This is becouse vector elements are stored in a contiguous computer memory space.

-- vectors have a specific capacity that can be predefined
my_vector: Vector(10) of Integer;

Vector literal

Vectors are represented by an enumeration of elements enclosed by square brackets [,,,] and delimited by comma. An empty vector is theoretical possible but not usable. When a vector is initialized the capacity of the vector will be the same with the number of elements in the vector literal.

Vector initialization

-- vector initialization with capacity of 10 elements
my_vector: Vector(10):0 of Integer;
-- vector initialization with literal with capacity of 10 elements
my_vector=[0,1,2,3,4,5,6,7,8,9]:Vector of Integer;

Deferred initialization

A vector can be declared without the capacity. Vector capacity can be establish later.
In the implementation the vector capacity can be establish using the vector() constructor.

-- vectors deferred initialization
procedure test(n:Integer) is
   my_vector: Vector of Integer;
begin
   my_vector:=Vector(n);
end procedure;

Working with elements

-- declare a vector of 10 elements for this example
procedure test is
  my_vector=[0,1,2,3,4,5,6,7,8,9]:Vector(10) of Integer;
begin
  -- vector elements are identified using notation vector[index]
  if my_vector[0] == 0 then
    print (“This is the first element: ” & my_vector[0]);
  end if;
end procedure;

 

Note: To resize a vector a new vector must be created in memory. The old vector values must be copy over to the new reserved location. The old memory location must be free once the values are moved. Therefore we need to plan ahead the vector capacity.


Matrix

A matrix is a multidimensional array that is a collection of elements organized in rows and columns. Usually a matrix have two logical dimensions but multiple dimensions are possible.A matrix has a predefined capacity and ocupy a fixed memory size. The elements of a matrix can be initialized using a matrix literal. This is represented similar to a vector using square brackets:

Syntax:

<var_name>: Matrix(n,m) of <type>;

Example:

-- define a matrix m that has 2 dimensions with 3 rows and 3 columns.
let
  m:Matrix(3,3) of String; 
do
  m=[['a0','b0','c0'],['a1','b1','c1'],['a2','b2','c2']];
done;

Deferred Initialization

-- the capacity initialization can be postponed for run-time
let
  <var_name>:Matrix of <type>;
do
  <var_name>:=Matrix(n,m); !establish dimensions
end do;

Addressing elements

Matrix elements can be addressed by subscript starting from 0 as first element:

var_name[0,0] !is the first element of the matrix.
var_name[n,m] !is the last element of a matrix.

Modify all elements of the matrix is possible using assign operator “:”

! define a matrix having 2 rows and 2 columns
! initialize all elements with 100
let
  my_array=100:Matrix(2,2) of Integer;
do
  print(my_array);
end do;
100,100,100,100

Operations

Operations with matrix and vectors are possible and will be defined as in mathematics. A matrix can be initialized using literals, can be multiplied with a scalar and you can add a scalar to a matrix. A scalar is a single numeric value.

Example:

let 
  my_array=100:Matrix(2,2) of Integer; 
do
  my_array:=my_array + 10;
  print my_array; --> [[110,110],[110,110]]
  my_array:=(my_array-10) * 10;
  print my_array; --> [[1000,1000],[1000,1000]]
end do;

Note: Matrices are multidimensional while computer memory is a linear space. This is an impedance mismatch that require mapping. Some computer languages organize memory row by row and some others organize memory column by column. Therefore passing a memory matrix from one computer language into another require a transposition that can be a performance bottlenecks.

Note: Level language organize the memory row by row. That means elements from the same row are placed side by side from first row element to last row element. Then the second row and so on. The whole matrix must occupy a contiguous space in memory for program efficiency.


Record

A record is a composite data type and represent a group of elements. The elements can be native types, collections or other records. So a record can be a nested structure of elements. A variable can be defined based on a record type.

-- record specification syntax
variable 
  <record_name>:Record of (<member_name> type [,<member_name> type]...);

Record usage

  • A record can be used to store an entity that have attributes
  • A record can be used as parameter into a function or procedure
  • A function can return a record variable as a result
  • A vector or a matrix can contain a collection of records

Record literals

A record literal is enclosed in parentheses (,,,) having elements separated by coma. Elements of a record literal are represented using pairs separated by “:” like name:value. The record literal can span one or many lines and is more or less similar to JSON notation.

Example of record literal:

(
  Name: "John",
  Address: (
    streetAddress: "21 2nd Street",
    city" "New York",
    state" "NY",
    postalCode: "10021-3100"
  )
)

Record variables

A record variable is a reference to a record structure. The memory is allocated using the initialization operator “:” The left member is the variable name and the right member is a literal or a function that will return a record structure.

Example:

  -- we declare a recod type  
type 
  Person:Record of (      
    name:String,
    age: Integer
  );

-- we create two variables based on person type
procedure main is
  person1=(name:"John", age:21):Person;
  person2=(name:"Vera", age:20):Person;
  person:Person;
  catalog:Vector(30) of Person;
begin
  -- initialize one persona
  person := (name:"John Doe", age:20);

  -- create a vector with 30 copy of person record
  let
    i:Integer;
  loop
    when i==30 exit;
    catalog[i]:=person;     
  end loop;
end program;

executing the program test:

lev:>run test
John and Vera are lovers.

Complex records

A variable of type record can contain a collection of elements or it can be nested forming complex recods.

Syntax:

type <type_name> Record of (
   <member_name>:<type>, 
   <member_name>:<type>,
   ...
   <vector_member>:Vector(n) of Record(<member_name>:<type>,  <member_name>:<type>)
   );

Literal example

In this example we show a complex record. The Address is a nested record. phoneNumbers is a Vector of records.

(
  Name: "John",
  Address: (
    streetAddress: "21 2nd Street",
    city: "New York",
    state: "NY",
    postalCode: "10021-3100"
  ),
  phoneNumbers: [(type: "home", number: "212 555-1234"),(type: "office", number: "646 555-4567")]
)

Deferred Initialization

procedure main is
  type Person: Record of (name:String, age:Integer);
  variable person1, person2 :Person;
begin
  Person1:= (name:“John”, "age":”21”);
  Person2:= (name:“Vera”,"age":”20”);
  print(“#s and #s are lovers.” <- (person1.name, person2.name));
end procedure;
level:>run main
John and Vera are lovers.

Read Next: Level: Program Sections;