Type conversions

SPL is statically typed and strongly typed and has structural equivalence.

SPL adopts a strong static type system, because it saves time for runtime checks, and errors that are not prevented statically are difficult to track down in a distributed system. The static typing is enforced by explicit type declarations and compile-time type checking. The strong typing is enforced by providing only few implicit conversions between types. Structural equivalence means that types of the same structural composition are considered identical. SPL uses structural equivalence, because that facilitates converting data to and from external applications, files, or databases that do not share the same type system. While structural equivalence can lead to types considered equivalent even when they were intended to differ, SPL has structural equivalence to avoid adapter bloat, which can slow down programs and clutter up code. Instead of implicit conversions, SPL offers explicit conversions (casts) where it makes sense. Explicit conversions look like in Java or C, for example, (int32)2.5 returns 2.

SPL allows the following explicit type conversions:

  • From any type to the same type (identity conversion).
  • From any type to any string type and back (string conversion). These conversions behave like they first serialize to UTF-8, and then convert to the target type.
  • From an xml to an rstring and back.
  • From a tuple to an xml and back. A tuple converted to an xml uses the schema found in $STREAMS_INSTALL/etc/xsd/SPL/serializedTupleModel.xsd.
  • From a blob to a bounded or unbounded list<uint8> and back.
  • From any enumeration type to any integral type and back. In conversions, enumerations are numbered from zero. For example, given type t = enum{a,b,c};, then (int32)a == 0, and (t)1 == b.
  • From any non-complex number type to any other non-complex number type. Widening a signed integral type to a larger signed integral type performs sign-extension on the two's complement representation. Other integral widenings zero-extend the representation. All integral narrowings perform truncation, discarding higher-order bits. Conversions from integers to floating-point numbers round to the nearest value, and conversions from floating-point numbers to integers round towards zero. Although numeric conversions might lose information due to rounding, overflow, and so on, they never cause a runtime exception.
  • From time stamp to float64 and back, where the float64 represents seconds.
  • From a list of two non-complex numbers to a complex number.
  • From any complex number type to any other complex number type.
  • From any list type to any other list type, bounded or unbounded, if the element types are either identical, or the element types are convertible and primitive.
  • From any set type to any other set type, bounded or unbounded, if the element types are either identical or convertible and primitive.
  • From any map type to any other map type, bounded or unbounded, if the key and value types are either identical or convertible and primitive.
  • From a tuple type WideT to a tuple type NarrowT if WideT has a superset of the attributes of NarrowT, in the same order. The conversion discards excess attributes and cannot be reverted. You can use the assignFrom function in the SPL standard toolkit to assign matching attribute values from one tuple type to another.
  • From the empty tuple literal {} to any tuple type.
  • From variables and attributes of non-optional type T to variables and attributes of optional type with the same underlying type T.
  • SPL does not permit conversions from and to Boolean. For example, (boolean)Myint is not allowed because it is shorter and less ambiguous to use integer comparison such as Myint!=0. SPL also does not permit conversions from ustring to xml or back. ustring casts to xml must go through rstring.

For optional types, SPL does not permit type conversions from variables or attributes of optional type to variables and attributes of non-optional type.

When SPL converts to bounded strings or lists, it drops excess elements at indexes that exceed the bound. When SPL converts to bounded sets or maps, and the size exceeds the bound, it drops elements too, but the specific dropped elements are implementation-dependent.

Types in SPL are equivalent if they are the same primitive types, or if they are composed from equivalent types using the same type constructor (and in case of tuples, the same attribute names in the same order). For example, variables of types LocT1 and LocT2 in the following code can be assigned to each other:
void test() {
  type LocT1 = int32 x, int32 y;
  type LocT2 = int32 x, int32 y; // same attributes in same order
  LocT1 loc1 = { x=1, y=2 };
  mutable LocT2 loc2;
  loc2 = loc1;                   // this is legal
}

As far as SPL is concerned, LocT1 and LocT2 are the same type; the fact that they have different names is irrelevant. Therefore, the assignment between variables loc1 and loc2 is legal, and does not constitute a type conversion. (For language experts: The alternative to structural equivalence is name equivalence. For example, in Java, two classes with different names are not equivalent even if they have the same attribute names, types, and order. Since SPL types describe pure data without behavior, and types are often anonymous and declared inline, structural equivalence made more sense for SPL.)

SPL permits implicit conversions only in two places: subscripts and variable initializers. For an example of an implicit type conversion in a subscript, let v be a list. Lists indexes are always uint32, so the subscript v[9] implicitly converts from int32 to uint32, as if it was written v[(uint32)9]. An out-of-bounds subscript causes a runtime error, independently of whether there was an implicit conversion involved. Not all subscripts are convertible. For example, subscripting a list v with a Boolean v[true] is a compile-time error.

An example of an implicit type conversion in a variable initializer is int8 x = 3;. This code implicitly converts from int32 to int8, as if it was written int8 x = (int8)3;. (For language experts: Implicit conversion in initializers prevents what is known as type stuttering, which is unnecessary repetition of the type in the initializer.)

Unlike some other languages, in other contexts, SPL has no implicit conversion from int to string (no "num"+1), from int to float (no 1+2.0), or from int to boolean (no while(1)...). To perform these conversions, you must make them explicit: (rstring)1, or (float64)1, or true.