10. Type Casting
Gazprea provides explicit type casting. Type casting is an
expression. A value may be converted to a different type using the
following syntax where value is an expression and toType is the
destination type:
as<toType>(value)
Conversion from one type to another is not always legal. For instance
converting from an integer array to an integer has no
reasonable conversion. Attempting such a conversion is a compile-time
error; the compiler must emit a TypeError (see Errors). More
generally, any as<> conversion this chapter does not describe as legal is
a compile-time error, and the compiler must emit a TypeError (see
Errors).
10.1. Scalar to Scalar
This table summarizes all of the conversion rules between scalar types
where N/A means no conversion is possible, id means no change is
necessary, and anything else describes how to convert the value to the
new type. Attempting a conversion marked N/A is a compile-time error;
the compiler must emit a TypeError (see Errors):
To type |
|||||
From type |
boolean |
character |
integer |
real |
|
boolean |
id |
‘\0’ if false, 0x01 otherwise |
1 if true, 0 otherwise |
1.0 if true, 0.0 otherwise |
|
character |
false if ‘\0’, true otherwise |
id |
unsigned byte (0-255) |
unsigned byte (0-255) |
|
integer |
false if 0, true otherwise |
unsigned integer value mod 256 |
id |
real version of integer |
|
real |
N/A |
N/A |
truncate |
id |
|
A character is interpreted as an unsigned byte when cast to a numeric
type, so character to integer (or real) yields a value in 0 to
255 – for example as<integer>('\xFF') is 255, not -1. This is
the inverse of the integer to character rule, under which an integer
n becomes the byte n reduced modulo 256 into the range 0 to 255
– the mathematical, non-negative remainder, so as<character>(-1) is
0xFF (= 255) and as<character>(256) is 0x00 (the null character).
For printable ASCII characters (0 to 127) this is exactly the ASCII
code.
10.2. Scalar to Array
A scalar may be explicitly cast to an array of any dimension with an element type that the original scalar can be explicitly cast to according to the rules in Scalar to Scalar. A scalar to array cast must include a size with the type to cast to as this cannot be inferred from the scalar value. For example:
// Create an array of reals with length three where all values are 1.0.
real[*] v = as<real[3]>(1);
// Create an array of booleans with length 10 where all values are true.
var u = as<boolean[10]>('c');
v -> std_output;
'\n' -> std_output;
u -> std_output;
Output
[1 1 1]
[T T T T T T T T T T]
10.3. Array to Array
Conversions between array types are also possible. First, the values of the
original are cast to the destination type’s element type according to the rules
in Scalar to Scalar and then the destination is padded with
destination element type’s zero value or truncated to match the
destination type size. Note that a concrete size is not required for array to
array casting: writing the destination element type with an unspecified length
([*]) keeps the old size, so no padding or truncation occurs. Padding or
truncation happens only when a concrete size is given. For example:
real[3] v = [i in 1..3 | i + 0.3 * i];
// Convert the real array to an integer array.
integer[3] u = as<integer[*]>(v);
// Convert to integers and zero pad.
integer[5] x = as<integer[5]>(v);
// Truncate the array.
real[2] y = as<real[2]>(v);
u -> std_output; '\n' -> std_output;
x -> std_output; '\n' -> std_output;
y -> std_output;
Output
[1 2 3]
[1 2 3 0 0]
[1.3 2.6]
A cast of the empty array literal [] (as opposed to a typed variable
holding an empty vector) is ill-formed, because a literal empty array
does not have a type; the compiler must emit a
TypeError (see Errors).
10.4. Multi-dimensional Arrays
Conversions between arrays of any dimension are possible. Such a cast preserves rank (the result has the same number of dimensions as the operand); only the extents and element type change, just as an array is never implicitly cast to a different rank (see Implicit Casts). The process is exactly like Array to Array except padding and truncation can occur in all dimensions. For example:
real[2][2] a = [[1.2, 24], [-13e2, 4.0]];
// Convert to an integer matrix.
integer[2][2] b = as<integer[2][2]>(a);
// Convert to integers and pad in both dimensions.
integer[3][3] c = as<integer[3][3]>(a);
// Truncate in one dimension and pad in the other.
real[1][3] d = as<real[1][3]>(a);
real[3][1] e = as<real[3][1]>(a);
b -> std_output; '\n' -> std_output;
c -> std_output; '\n' -> std_output;
d -> std_output; '\n' -> std_output;
e -> std_output;
Output
[[1 24] [-1300 4]]
[[1 24 0] [-1300 4 0] [0 0 0]]
[[1.2 24 0]]
[[1.2] [-1300] [0]]
10.5. Array and Vector
A vector participates in as<> casts on both sides.
As the operand of an array cast, a vector supplies its current length as the source size; the cast then pads with the element type’s zero value or truncates to the destination array’s stated size, exactly as in Array to Array.
As the destination type, a
vector<T>takes no size specifier: the result simply has the length of the value being cast, so there is nothing to pad or truncate. Only the element type is converted, per Scalar to Scalar.A scalar may be cast directly to a
vector<T>destination, producing a single-element vector. Because a vector carries no size specifier, the element typeTmust be written explicitly – there is no size or element-type inference for this cast.
vector<real> v = [1.5, 2.5, 3.5];
// Vector as operand: its current length (3) is the source size.
integer[2] a = as<integer[2]>(v); // [1, 2] (truncated)
integer[5] b = as<integer[5]>(v); // [1, 2, 3, 0, 0] (padded)
// Vector as destination: no size; takes the value's length.
integer[3] w = [4, 5, 6];
vector<integer> u = as<vector<integer> >(w); // [4, 5, 6]
// Scalar to vector: single-element vector; T must be explicit.
vector<integer> s = as<vector<integer> >(5); // [5]
10.6. Tuple to Tuple
Conversions between tuple types are also possible. The source type and
the destination type must have an equal number of members, and each member
must be pairwise castable. A mismatch in the number of members, or a member
that cannot be cast under its own kind’s rule, is a compile-time error and
the compiler must emit a TypeError (see Errors). Every
member is cast by the rule for its own kind: scalar members follow
Scalar to Scalar, array members
follow Array to Array (including padding and truncation), and
a nested tuple, vector, or array member follows the same
cast rules as a standalone value of that type. A struct member is the
exception: a struct cannot be cast (see Structs), so the two
struct types must be identical and the member is copied unchanged. For example:
tuple(integer, integer) int_tup = (1, 2);
tuple(real, boolean) rb_tup = as<tuple(real, boolean)>(int_tup);
rb_tup.1 -> std_output; '\n' -> std_output;
rb_tup.2 -> std_output;
Output
1
T
10.7. Structs
Structs are nominal types, so they cannot be cast to other types.