Qore Programming Language Reference Manual: Container Data Types

Qore Programming Language Reference Manual 0.8.11

- Main Page
- Related Pages
- Modules
- Namespaces
- Classes

All Classes Namespaces Functions Variables Groups Pages
Container Data Types

Qore supports three types of container types (see also Basic Data Types and Code Data Types):

- lists
- hashes (associative arrays)
- objects (see Object and Classes for more information)

These container types can be combined to make arbitrarily complex data structures. The data type of any element can be any basic type or another aggregate type. The types do not have to be uniform in one container structure.

# List

Description:
  Lists (or arrays) are simply ordered containers of values. A list element can be any Qore type (even another list, hash, or object).
  Lists are specified by giving expressions separated by commas as follows:
  $list = (expression, expression [, expression ...]);
  Here is a concrete example:
  my list $list = (
  1,
  2,
  "three",
  4.0,
  5e20n,
  6,
  2001-01-15Z,
  );
  Trailing commas can be left on the end of a list (or a hash), making it easier to insert and remove elements at the end of a multi-line list.
  List elements are dereferenced using square brackets: `"["` and `"]"`. The first element in a list has index zero.
  Example:
  $element3 = $list[2];
  The following operators perform special processing on lists:
  - elements
  - shift
  - unshift
  - push
  - pop
  - splice
  - []
  - +
  - +=
  - map
  - foldl
  - foldr
  - select

Immediate Value Example:

      (1, "two", 3.0)

Pseudo Class for Type List:
  <list>

Type Code:
  Qore::NT_LIST

Type Name:
  `"list"`

See Also
  - list, softlist
  - List Functions

Note
  - Trailing commas can be left on the end of a list (or a hash), making it easier to insert and remove elements at the end of a multi-line list.
  - List elements are dereferenced using square brackets: `"["` and `"]"`. The first element in a list has index zero.
  - Dereferencing an invalid element (past the end of the list or with a negative number) will return NOTHING
  - Use <list>::iterator() as an easy way to get a list iterator object for the current list

# Hash

Description:
  Hashes are containers that associate values to a string key and also preserve key order for consistent data serialization/deserialization. Hash key lookups are case-sensitive and use a hashing algorithm that in the worst case should provide O(ln(n)) complexity to execute; in the base case finding a particular key in a hash is executed in constant time (ie O(1) complexity).
  Hashes are specified using the following syntax:
  $hash = (
  "key1" : expression,
  "key2" : expression,
  ...,
  );
  Here is a concrete example:
  my hash $hash = (
  "apple" : 1 + 1,
  "pear" : "good",
  );
  Trailing commas are ignored (as with lists) to allow for easier insertion and deletion of elements in source code.
  Hashes are dereferenced in one of two ways, either using curly brackets: `"{"` and `"}"`, where any valid Qore expression can be used, or using the dot "." hash member dereferencing operator, where literal strings can be used.
  $element3 = $hash{"pe" + "ar"};
  Is equivalent to:
  $element3 = $hash.pear;
  and:
  $element3 = $hash."pear";
  and:
  $element3 = $hash.("pe" + "ar");
  Hash members can have the names of keywords or names that are not valid identifiers, but in this case to dereference them you cannot use the dot operator with a literal string, otherwise a parse error will be raised. Use quotes around the member name when dereferencing hash members with the same name as a qore keyword or other name that is not a valid identifier as follows:
  $element3 = $hash."keys";
  $element3 = $hash{"this-element-1!"};
  A literal string after the dot "." hash member dereferencing operator must be a valid Qore identifier; therefore if you want to use a hash key that's not a valid identifier, enclose the string in quotes.
  If you want to use the result of an expression to dereference the hash, then either the curly bracket syntax must be used or the expression must be enclosed in parentheses.
  In the case of using a literal string with the dot operator, keep in mind that the string is always interpreted as a literal string name of the member, even if there is a constant with the same name. To use the value of a constant to dereference a hash, use curly brackets with the constant: ex:

      $hash{MyConstant}

  Note that hash keys can also be given by constants (as long as the constant resolves to a string) when using curly brackets.

Immediate Value Example:

      ("key1": 1, "key2": "two", "key3": 3.141592653589793238462643383279502884195n)

Pseudo Class for Type Hash:
  <hash>

Type Code:
  Qore::NT_HASH

Type Name:
  `"hash"`

See Also
  hash

Note
  - Trailing commas are ignored in immediate hash declarations (as with lists) to allow for easier insertion and deletion of elements in source code.
  - Hashes are dereferenced in one of two ways, either using curly brackets: `"{"` and `"}"`, where any valid Qore expression can be used, or using the dot "." hash member dereferencing operator, where literal strings can be used. In the case of using a literal string with the dot operator, keep in mind that the string is always interpreted as a literal string name of the member, even if there is a constant with the same name. To use the value of a constant to dereference a hash, use curly brackets with the constant: ex:
    $hash{MyConstant}
  - Use quotes around the member name when dereferencing hash members with the same name as a qore keyword or other name that is not a valid identifier: ex:
    $hash."my-tag-1!"
  - Qore hashes preserve key insertion order to support consistent serialization and deserialization to strings (such as XML, JSON, or YAML strings) or data encoding formats, therefore the keys operator and the <hash>::keys() pseudo-method will always return the hash keys in insertion/creation order
  - Dereferencing hash values that are not present in the hash will return NOTHING; to catch typos in hash member names when dereferencing a hash, you can use an object and declare a list of allowed public members in the class definition (in which case dereferencing a non-existant member will cause a parse exception to be thrown, if the object's class is known at parse time, otherwise it will cause a runtime exception), also the Qore::ListHashIterator class allows for hash members to be dereferenced transparently and will throw an exception if a non-existant member name is given (to catch typos).
  - There are several pseudo-methods implemented in both the <hash> and the <nothing> pseudo-classes that provide asy access to special hash iterators:
    - <hash>::iterator() provides easy access to a hash value iterator; it returns a HashIterator object where the HashIterator::getValue() method returns the current key value
    - <hash>::keyIterator() provides easy access to a hash key iterator; it returns a HashKeyIterator object where the HashKeyIterator::getValue() method returns the current key string; this is very useful when using a hash as a simulation for a set of strings and quickly iterating the hash with the map operator, for example
    - <hash>::pairIterator() provides easy access to a hash key/value pair iterator; it returns a HashPairIterator object where the HashPairIterator::getValue() method returns a hash with `"key"` and `"value"` keys giving the key-value pair for the current iterator position in the hash
    - <hash>::contextIterator() provides easy access to the current element in a hash of lists such as that returned by the Datasource::select() or DatasourcePool::select() methods; it returns a HashListIterator object where the HashListIterator::getValue() method returns a hash of the current row value (which is very useful when iterating query results with the map operator, for example); this class also provides a memberGate() method that allows the iterator object itself to be dereferenced directly as a hash representing the current row (which is useful in foreach statements for example).
    - the corresponding pseudo-methods in the <nothing> pseudo-class return empty SingleValueIterator objects so that these pseudo-methods can be safely used with *hash (ie "hash or nothing") value types.

# Object

Description:
  Qore objects are instantiations of a class. They have members (like a hash; values associated to string keys), and methods. The class definition specifies the methods that run on objects of that class, public and private members, static methods and variables, etc associated with the class (however note that static methods do not run in the scope of an object). See Classes for information on how to create a class in Qore.

Immediate Value Example:
  new Mutex();

Pseudo Class for Type Object:
  <object>

Type Code:
  Qore::NT_OBJECT

Type Name:
  `"object"`

See Also
  - object
  - Object Functions

Note
  each Qore type has a "pseudo-class" associated with it (the default is <value>); methods from the data type's "pseudo-class" can be run on any value of that type; see "Pseudo Class for Type" headings in Basic Data Types for more information. Pseudo-methods can be overridden in classes; if a class implements a method with the same name and signature as an object pseudo-method, then the class' method will be executed instead of the pseudo-method.

## Object Overview

The recommend way to instantiate an object is to declare its type and give constructor arguments after the variable name in parentheses as follows:

my class_name_or_path $var_name([argument list])

For example (for a constructor taking no arguments or having only default values for the aguments, the list is empty):

my Mutex $m();

If parse option %new-style is set, the above example declaring a local variable of class Qore::Thread::Mutex would look as follows:

Mutex m();

Objects can also be instantiated using the new operator as follows.

new class_identifier([argument list])

For example:

my Mutex $m = new Mutex();

Or, with parse option new-style:

Mutex m = new Mutex();

Objects have named data members that are referenced like hash elements, although this behavior can be modified for objects using the memberGate() method. Object members are accessed by appending a dot '.' and the member name to the object reference as follows:

object_reference.member_name

For more information, see Class Members.

Object methods are called by appending a dot '.' and a method name to the object reference as follows:

object_reference.method_name([argument_list])

Or, from within the class code itself to call another method from inside the same class hierarchy:

$.method_name([argument_list])

Or, with parse option new-style, the method call is made without the "$.":

method_name([argument_list])

For more information, see Object Method Calls.

The object references above are normally variable references holding an object, but could be any expression that returns an object, such as a new expression or even a function call.

Note
  Objects are treated differently than other Qore data types; they are only explicitly copied (see Object References for more informaion). Any object instantiated with the new operator will remain unique until deleted or explicitly copied. An explicit copy is made with the copy method, and does not always guarantee an exact copy of the source object (it depends on the definition of the copy method for the class in question).

Objects exist until they go out of scope, are explicitly deleted, or their last thread exits. For detailed information, see Classes.

See Also
  object

## Object References

In Qore objects are treated differently from all other data types in that they are by default passed as arguments to functions and methods by passing a copy of a reference to the object (similar to Java's handling of objects). That means that passing an object to a function that modifies the object will by default modify the original object and not a copy, however reassigning a local parameter variable assigned an object passed as an argument (that is only assigned to a local variable in the calling function) will not result in deleting the object, but rather decrement its scope reference count (note that if the object were created as a part of the call and reassigning the variable would cause the object's scope reference count to reach zero, then the object would be deleted in this case).

Assigning an object to a variable has the same effect; a copy of a reference to the object is assigned to the variable. This results in prolonging the object's scope (by owning a new copy of a reference to the object).

An example:

sub test2(any $x) {
# we can modify the original object like so:
$x.member = "tree";
# here we re-assign $x, but since the object is also assigned
# to $o in the calling function, the object's scope is still
# valid, and therefore nothing happens so the object
$x = 1;
}
sub test() {
my TestObject $o();
# here we pass a copy of a reference to the object in $o
test2($o);
# this will print out "ok\n", because the object is still
# valid and the member has been set by test2()
if ($o.member == "tree")
print("ok\n");
}
# when test() exits, the object in $o will go out of scope
# and be deleted

If, however, an object is passed by reference, then the local variable of the called function that accepts the object owns the scope reference of the calling functions's variable.

An example:

sub test2(any $x) {
# we can modify the original object like so:
$x.member = "tree";
# here we re-assign $x, and since we own the only scope
# reference to the object, the object will go out of
# scope here and be deleted
$x = 1;
}
sub test() {
my TestObject $o();
# here we pass a reference to the object in $o
test2(\$o);
# the object has already been deleted in test2() and
# therefore nothing will be printed out
if ($o.member == "tree")
print("ok\n");
}

Note
  that when parse option %allow-bare-refs is set, then variable references as in the above examples are made without the `"$"` character.

## Object Scope

Objects are automatically deleted when their scope-relevant reference count reaches zero (note that objects can be deleted manually at any time by using the delete operator). Whenever an object is deleted, the object's class' destructor method is run on the object.

The following affect objects' scope:

- variable assignments: an object's automatic scope is prolonged as long as the object is assigned to a local variable
- object method thread launched within the object: if a member function thread was launched from within the object using the background operator, the object's automatic scope is prolonged to the life of the new thread. Object threads started externally to the object (i.e. not directly from an expression with the background operator within a method) will not prolong the scope of the object.

Note
  If an object with running threads is explicitly deleted, and this case is not handled in the object's destructor() method (by ensuring that all other running threads terminate gracefully), exceptions will be thrown in other threads at any attempt to access the already-deleted object.
  The fact that object threads and closures can prolong object scope means, for example, that objects assigned to local variables can exist for longer than the scope of their host variable if they have one or more methods running in other threads or if closures created from within the object still exist at the time the local variable goes out of scope.
  For more information about threading, please see Threading

Since
  As of Qore 0.8.10 the existence of a closure created within the object no longer prolongs the scope of the object; the closure encapsulate the object's state (along with any local variables referenced within the closure), but if the object goes out of scope while the closure still exists, then any references to the object after the object has been destroyed will cause `OBJECT-ALREADY-DELETED` exceptions to be thrown. This addresses memory and reference leaks caused by recursive references when closures encapsulating an object's scope are assigned to or accessible from members of the object.

## Copying Objects

To explicitly generate a copy of an object, the `copy()` constructor must be called. This is a special method that exists implicitly for every class even if it is not explicitly defined (like `constructor()` and `destructor()` methods). The implicit behavior of the `copy()` constructor is to create a new object with new members that are copies of the original members (except objects are created as copies of references to the object). Then, if any `copy()` method exists, it will be executed in the new object, passing a copy of a reference to the old object as the first argument to the `copy()` method.

Note
  In a class hierarchy copy() methods are called in the same order as constructor() methods.
  Not all built-in classes can be copied. Classes not supporting copying will throw an exception when the `copy()` methods are called. See the documentation for each class for more information.