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 anrstring
and back. - From a
tuple
to anxml
and back. Atuple
converted to anxml
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 thefloat64
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 typeNarrowT
ifWideT
has a superset of the attributes ofNarrowT
, in the same order. The conversion discards excess attributes and cannot be reverted. You can use theassignFrom
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 typeT
. - 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 asMyint!=0
. SPL also does not permit conversions fromustring
toxml
or back.ustring
casts toxml
must go throughrstring
.
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.
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
.