Optional types

In SPL, an optional type is used when a variable or attribute might have no data value associated with it because the value is unavailable or unknown. When there is no data value for a variable or attribute, and an optional type is used, the value of that variable or attribute can be set to null.

Optional types are composite types, along with tuple and collection types, and are parameterized as such using the underlying type. The underlying type is the type of the data value when the variable or attribute has an associated data value.

Use the form optional<T> to declare variables or attributes in SPL that can have either a null value or a value of the underlying type T. For example, you can declare an optional int32 integer in a stream, list, or tuple, as follows:

stream<int32 i, optional<int32> j> Src = MySource() {}
mutable list<optional<int32>> myList;
type T = tuple<int32 i, optional<int32> j>;

A null value has a literal representation for every type that can express a literal value. The characters null represent a null value.

The following example declares a tuple variable t and initializes the j attribute to null:
tuple<int32 i, optional<int32> j> t = { 1, null };

The following example declares and initializes a mutable integer variable to null:

mutable option<int32> i = null;
You can assign a null value to an optional variable or attribute that has had a non-null value:
mutable optional<int32> i = 5;
i = null;
Notice: If you assign a null value to a variable or attribute that is of a non-optional type, you will get a compiler error, or a runtime exception.
You can use an equality comparison to test if a variable or attribute has a null value assigned. For example:
if (var == null) { }

Default initialization

When an operator returns a tuple that is default initialized, for example, a Beacon operator without an output clause, tuple attributes are initialized with their default values. The default value for an attribute of optional type is null.

Value assignment

In SPL, variables and attributes of optional type can be directly assigned to each other if they have the same underlying type. In addition, variables and attributes of a non-optional type can be directly assigned to variables or attributes of an optional type provided the underlying type is the same as the non-optional type. Variables and attributes of an optional type cannot be directly assigned to variables and attributes of a non-optional type. For example:
optional<int32> i = 42;
or
optional<int32> j = i;

The unwrap operator is used to access the non-null value of an optional type for the purposes of assignment to a non-optional type. For example:

int32 k = j!;

The unwrap operator will cause a runtime error if the value of j is null.

The unwrap-or-else operator is used to assign from an optional type to a non-optional type when the value of the optional type might be null.

In the following example, if the value of j is null, k is assigned the default value of 4:
int32 k = j ?: 4;

Automatic promotions

To simplify the use of optional types, Teracloud® Streams will perform automatic promotions from T to optional<T>. There are two cases where values are automatically promoted from T to optional<T>:
Literal null
null is automatically promoted to optional<T> if the type T can be determined. For example:
optional<int32> i = null;

In this case the type can be determined, and the literal null is promoted to type optional<int32>.

Expression with optional and non-optional types
Another case for promotion is when an optional type and a non-optional type appear in an expression. For example:
optional<int32> i = 5;
In this case the literal 5, which is of type int32, is automatically promoted to optional<int32>. Another example:
if (i == 5) { } // i is of type optional<int32>
In this case, 5 is promoted to the type optional<int32>.
In the case of list, map, and set literals, promotions occur as necessary on the list, map or set items and the overall resulting literal. For example:
mutable optional<map<optional<int32>, optional<rstring>> m1 = null;
m1 = {1:"a"}; // legal
Note: There is no automatic promotion from optional<T> to T.
In cases where promotion is required and the optional type cannot be determined, there will be no automatic promotion. In these cases you can use casts to avoid compiler errors. For example, if the type of an operator parameter of optional type cannot be determined (by using any types declared in the operator model for native operators, or using declared expression types on expressions used in composite operators), a cast will be required, as in the following example where a cast is used for setting the parameter p2 to null:
 stream<optional<int32> i, optional<int32> j> A = myOp() {
      param
        p1: null;
        p2: (optional<int32>) null;
    }
    ..
    composite myOp(output Out) {
        param
            expression<optional<int32>> $p1;
            expression $p2;
        graph
        stream<Types> Out = Custom() {
          logic onProcess:
            submit({i=$p1, j=$p2},Out);
        }
    }

Operators for optional types

isPresent
The isPresent operator, (??) checks if a variable or attribute of optional type is non-null. The isPresent operator returns true if the variable or attribute of optional type is non-null and false if the variable or attribute is null. For example:
 if (var??) { }
uwrap
The unwrap operator (!) accesses the data value of an optional type when the value of the optional type is not null. For example:
optional<int32> x = someFunctionThatReturnsAnOptional();
int32 y = x!;
When the value of the optional type is null, you will get a runtime exception.
unwrapOrElse
The unwrapOrElse (?:) operator gets the data value of an optional type if the value is not null. For example:
int32 i = optional_var ?: 0;.

The value after the ?: acts as a default data value that is used when the value of the optional type is null.

You can use these operators with expressions of non-optional type. The isPresent operator returns true for non-optional type expressions. The unwwrap operator returns the expression provided for non-optional type expressions. The unwrapOrElse operator returns the expression provided when given a non-optional type expression and never uses the default value.