Using generic types
Simple types are good for many purposes, but they are poor at modelling collection types such as arrays, maps and sets. Consider arrays as an example. An array of integers seems to be a different type from an array of strings, but they have many things in common.
Generic types allow types to have parameters that modify the base type. For example, the class Array<T> has a single type parameter T which describes the type of the array items. Array<Int> is an array of integers, while Array<Str> is an array of strings.
This section discusses using generic types. We discuss defining generic types and generic functions later in Sections Defining generic types and functions and Generic inheritance and generic interfaces.
The following two functions illustrate using different array types. They simply return the first item of the argument array:
def FirstInt(ai as Array<Int>) as Int return ai end def FirstStr(as as Array<Str>) as Str return as end
The type of the first return value, ai is Int, since ai is an array of integers. Correspondingly the type of the second return value, as, is Str.
Both of the array types in the previous example derive from the same generic type Array<T>; here T is a type variable that can be replaced with any type to create a type instance. All type instances have the same members, but any type variables in the types of the members are replaced with the type parameters of the type instance. For example, the return value of the Array  operator is declared to be T (remember that the _get method implements the  operator behind the scenes; the return type of _get is T).
The example above did not create any arrays. Creating an array object with a specific item type is done using an expression of form [...] as <...>. The item type is within angle brackets < > while the array items are within the square brackets:
var a as Array<Int> a = [1, 3, 5] as <Int> -- Create integer array with 3 items a -- 3
You can often leave out the as <...> portion when creating arrays. In this case the array item type is inferred:
var a as Array<Int> a = [1, 3, 5] -- Ok a =  -- Ok
The most common case where you have to give the item type is when you initialize a local variable with an empty array:
def F() as void var a =  -- Cannot infer type of a a.append(1) end
You can correct the code by giving array item type explicitly:
def F() as void var a =  as <Int> -- Ok, infer Array<Int> as the type of a a.append(1) end
You can also define the type of a explicitly. In this case it is not necessary to give the array item type when creating the empty array:
def F() as void var a =  as Array<Int> -- Ok a.append(1) end
Note that in the first corrected example above the as <Int> construct applied to the preceding  expression only; the local variable type is inferred based on the type of the initializer. In the second example, the as Array<Int> construct refers to the variable declaration as a whole. It is equivalent to the following:
def F() as void var a as Array<Int> a =  a.append(1) end
You can an array value as the rvalue in multiple assignment as in dynamically-typed programs:
def F() as void var a = [1, 2] var b as Int var c as Int b, c = a -- Ok end
Multiple initialization is also possible:
def F(a as Array<Int>) var b, c = a -- Infer b and c to integers end F([1, 2]
We continue the discussion on multiple initialization later in Section Tuple types.
Map is a type with two generic type parameters, which is expressed by the full type name Map<KT, VT>. Here KT refers to key type and VT refers to value type. Using Map objects is straightforward in statically-typed code:
def F(m as Map<Str, Int>) m['foo'] = 5 m['bar'] = 3 Print(m['foo'] + m['bar']) end
Constructing Map object is similar to arrays, but there are two type arguments, and the arguments are given after the call to Map using as <...>:
var m as Map<Str, Int> m = Map() as <Str, Int>
Type inference is supported for type arguments:
var m = Map<Str, Int> m = Map() -- Ok def F() as void var m = Map(5 : 'foo', 3 : 'bar') -- Infer type to be Map<Int, Str> end
The Pair type is also a generic type, Pair<L, R>. Usually the type of a pair expression x : y can be inferred, for example:
def F() as void var p = 1 : 'foo' -- Pair<Int, Str> p.left -- Int p.right -- Str end
Sometimes it is necessary to specify type arguments explicitly. Do this using the as <...> construct, as for other generic types. In this case the Pair expression must be within parentheses due to operator precedence:
(nil : 'x') as <Str, Str> -- Pair<Str, Str>
Two instances of a generic type are compatible, if their arguments are identical. For example, the following code is valid, as it assigns a value of type Array<Int> into a variable of type Array<Int>:
var a = [1, 2, 3] as Array<Int> var b as Array<Int> b = a -- Ok
As always, assignment only copies references to objects, not objects itself. In the above example a and b will refer to the same Array object.
The type checker generates an error if the type arguments are different:
var a as Array<Int> var b as Array<Object> a =  b = a -- Error: incompatible types
As an exception, if the type arguments include type dynamic within them, the types may not have to be identical. You can also sometimes work around the above restrictions by using dynamic casts. Section The dynamic type and mixed typing discusses these topics.
Types which have a subtyping relationship (also generic ones) are also compatible. See Generic inheritance and generic interfaces for a discussion on this.
In the examples above, the type arguments of generic types were always simple types. Type arguments with more complex types are possible and often useful:
var a as Array<Array<Float>> -- Array of arrays var m as Map<Str, Array<Int>> -- Map from Str to Array of Int
You can use an array of arrays (such as a above) to represent a multidimensional array:
a = [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]]
Type inference supports complex types, as shown in the example above. Actually, type inference is especially useful when dealing with complex types, as otherwise programming can become very cumbersome. Compare the above fragment to an equivalent one with explicit types:
a = [[1.0, 0.0, 0.0] as <Float>, [0.0, 1.0, 0.0] as <Float>, [0.0, 0.0, 1.0] as <Float>] as <Array<Float>>
You may have figured out the pattern for constructing generic instances with explicit type arguments. You can construct an instance of arbitrary generic type X<T1, T2, ...> like this:
X(...) as <A1, A2, ...>
The above form is called a type application. The first ... refers to the arguments to the create method; they are ordinary values, not types. A1, A2, ... are types; they are the values of the type arguments T1, T2, etc.
For example, constructing an instance of Set<T> with explicit type arguments happens like this:
import set Set([1, 2, 6, 3]) as <Int>
As usual, the type application above is unnecessary as the type checker can infer the type argument.