Programming with Types
The Wolfram Compiler provides a powerful type system that assigns types to all elements in your code. This is used to guide compilation, detecting errors and generating optimal code. If you write polymorphic functions, the type system will select correct and optimal functions for the type parameters of your functions.
A key element of this involves being able to manipulate types in programs and is discussed in this section.
TypeSpecifier
TypeOf has many uses for working with types in compiled code. Here it is applied to a function argument:
Calling the function returns the result of TypeOf:
You can see the return type of the compiled function:
It is useful to distinguish between these two results.
The result of calling the compiled code was the expression TypeSpecifier["Integer64"].
The return type of the compiled code is a "TypeSpecifier" type. This is a parametrized type that wraps around other types, in this case an "Integer64":
When an instance of "TypeSpecifier" is serialized into an expression, it uses the head TypeSpecifier to wrap its argument.
The advantage of using "TypeSpecifier" to hold types in compiled code is that it ensures that these will not be confused with instances of the type. The latter might have special behavior, maybe for memory management.
You can create an instance of a "TypeSpecifier" type in compiled code by using the TypeSpecifier function:
This could be converted into an "InertExpression":
This is what happens when an instance of "TypeSpecifier" is returned to the Wolfram evaluator:
Another important feature of TypeOf is that it generates the type at compile time. This means that when it runs, it does not actually execute any code. The following example returns the type of the result of applying Print to an integer, which is the "Null" type, wrapped in "TypeSpecifier". It does not actually print anything:
Conversion
In the Wolfram Compiler, conversions between instances of types can use the function Cast:
This is the type of the function:
You can see what versions of Cast are available using CompilerInformation. There are many of them, and they are often written polymorphically (which can make it hard to see what is available):
You may wish to use a pattern search to narrow the results as discussed in this section.
Cast can also work with the result of TypeOf:
These are all quite helpful for writing polymorphic code. The following declaration is polymorphic and casts the first argument to the type of the second, with a print statement to show what it is doing:
This calls the function with the printed output:
Casting to the type of an argument can use TypeOf on the argument. If you want to cast to the type of the result, this is obviously harder. To access the return type of a function, you can use CurrentCompiledFunctionData["ReturnType"]. The following is an example:
This function calls the declaration that was just made. In order for this to work, the return type needs to be set. In the following example, this is done with TypeHint. In a real example, the code might be embedded in a declaration and the return type being set:
Calling the function gives the expected result:
If you want to create functions that convert between types, it is recommended that this be done by writing your own version of Cast.
In this example, two product types are declared, as well as a cast from one type to the other. Note how the cast makes explicit use of "TypeSpecifier"; this is how casts are written:
Compile a function that creates an instance of "Demo1", then call it, returning the result:
This compiles a function that uses the cast that was just written. When calling a cast, you do not need to use "TypeSpecifier"; this is done automatically for you:
Call the function, and a new object of type "Demo2" is created:
Other Casts
Other forms of cast exist to use lower-level conversions that can be used as building blocks. Generally, these use low-level intrinsic functions and are not suitable for declaring new versions.
This calls a conversion from an integer to a real, similar to the way that the C language converts. Generally, this never fails and just modifies the bit patterns in the number:
Calling the function makes the conversion:
Another type of cast is a "BitCast". This behaves as though the object was written to memory as one type and then read as another.
A bit cast from a float to an integer will generally return nonsense:
They can be useful to understand the bit patterns that are stored in a float:
A bit cast from a signed to an unsigned integer will typically return something recognizable if the input is positive:
However, if the input is negative, the result shows the underlying representation of integers:
With the appropriate shift, the input is returned:
Bit casts to and from "OpaqueRawPointer" can often be useful (especially when working with external libraries):
TypeHint
TypeHint is used inside code to guide results to a particular result. It can be used to give a typed constant, in cases to set the return type and in cases where type inferencing cannot find a solution.
Constants
A common case for using TypeHint is to create a constant of a particular type, for example, to make a zero of a type as in the following:
The return type shows that the type is actually an "UnsignedInteger8":
It is often quite useful to use a type computation such as TypeOf to generate the constant of correct type. This example would work well in a polymorphic definition:
Another form of constant is an array of type "CArray", as in the following. To return to the evaluator, it is cast to an opaque raw pointer:
This takes the pointer and casts it back to the "CArray".
Declare the Type
Another usage for TypeHint is to declare the type for some part of code. For example, this declaration returns a zero of a type set by the result:
However, a call to makeZero cannot be compiled. The return type has not been specified:

If TypeHint is used to set the result of the call, compilation works:
Maybe neater would be to use TypeOf inside the TypeHint:
It can be possible to avoid the use of TypeHint if the call is embedded inside another declaration.
This calls the declaration with an integer argument:
The result is reflected back if the input is positive:
If the input is negative, then a zero is returned. For this to compile, the zero must match type with the input:
The same declarations can be used with another type; in this example, a float:
The result is reflected back if the input is positive:
If the input is negative, then a zero is returned:
TypeHint and Evaluation
A key benefit of TypeHint is that it gets stripped out in interpreted code, so it does not interfere:
This makes it useful for code that wants to run in both interpreted and evaluated mode.
Literal Types
Literal types are types that use a literal value as the type. They are useful for holding information about some data and also for selecting different versions of a function. The "PackedArray" type uses a literal value to hold the rank of the array.
You can work with literal types quite easily in compiled code. This function has one argument, which is the literal type "name1":
The argument to this function has to be an instance of a LiteralType called "name1". This is entered here by wrapping the string in LiteralType, which makes it clearer this is not a string type being entered:
The function can be called with the string. This is due to the way that expression serialization is set up for literal types:
It is important to understand that this is not a type called "name1"; it is a literal type that has a value of "name1". You can see the type of the compiled code as below:
If the function was called with a different literal value, this would fail because the input type does not match:

You can create a literal type in compiled code by using the wrapper LiteralType:
Note that the value of a literal type must be known at compile time. So the following cannot be compiled:

You can extract the actual value stored in a literal type with Normal:
This returns the string value of the type:
Just to be clear, the type of the result of the compiled function is a "String", the value of which is the value in the literal type:
The value in a literal type itself has a type. They can be "String" as in the above example, "MachineInteger" or "Boolean":
This returns the value of the type:
Here a function that takes a different literal type is given:
This returns the value of the type:
Selecting Code
Literal types can be useful to select different versions of a function. Function selection uses the type, and so having a literal value for this can be useful.
Here are two declarations of a function called LiteralFunction. They both have the same name but different type signatures. One takes a literal type LiteralType["Version1"], and the other LiteralType["Version2"]:
The argument is LiteralType["Version1"], and this selects the appropriate function:
Now the argument is LiteralType["Version2"], and this selects the other function:
Another common use of LiteralType to select functions is in the declaration of Information, as will be seen later.
Type Parameter
Literal types can be used as type parameters to encode useful information about a type. In the following example a "SizedVector" type is declared. It is given a "Creator" and an operation to compute the length. A version of Information is provided to return the type that encodes the length. The value of this is extracted with Normal:
This creates an instance of the type, passing in the appropriate literal type value:
Calling the creator returns a reference to the type:
One of the useful things about encoding information in a type is that it makes sure the values are known at compile time. This means that the compiler can select optimal code:
The Wolfram Compiler uses this technique to implement the "PackedArray" and "NumericArray" types. For these, the rank of the array is held with a literal type. This allows a computation of the rank (using the function TensorRank), which generates the result at compile time:
Information
The compiler makes extensive usage of Information in order to extract information about compiled objects. Often, these are done for type computation purposes.
An example is shown below that extracts the "ElementType" from a "PackedArray". This shows the raw data that is stored in the array:
This can be contrasted with the "ContainedType" property, which returns the type that would come out of an iteration over the object. In this case, it is an "PackedArray"::["Real64",1]:
A feature of Information is that it can also take its argument wrapped in a "TypeSpecifier" type, as in this example:
Taking a "TypeSpecifier" is useful because it can work with functions such as TypeOf:
Implementing Information
You can implement your own version of Information. This is often useful to make your data types fit in with other polymorphic code for the Wolfram Compiler.
In this example, a product type will be declared along with support for the property "ElementType". First the type is declared:
This declares the function. Since this is a function that is only returning a type, it needs no implementation and can be written just with a type: