Wolfram Language Support
Numbers and Arithmetic | Lists |
Constants | Loop Structures |
Equality Comparisons | Functional Constructs |
Rectangular Data | Restrictions |
The Wolfram Language provides a powerful and flexible programming model that covers many different areas. Code tends to be very concise without the need for the many details that are required in other languages. When the Wolfram Compiler is used, it imposes more constraints than are present in interpreted Wolfram code. If these constraints are not met, then compilation will not work. However, if compilation works, it probably does what you want, and it should be fast.
This section looks at some of the ways that the Wolfram Compiler supports the Wolfram Language. As explained in the section on Compiler Input, you can explore whether Wolfram Language functions are supported with CompilerInformation.
Numbers and Arithmetic
The Wolfram Compiler supports numerical computation with support for integer, floating-point and complex numbers. Each of these is supported with different types. The concept of types is not natural to uncompiled Wolfram Language code, where there is only one type: the Wolfram expression. Types are a core aspect of compiled code and provide many benefits to the Wolfram Compiler.
Integer Types
The compiler supports a wide range of integer types, both signed and unsigned, and with a variety of sizes. A common type is the 64-bit integer, a common "natural" type for many architectures:
The compiled code can be executed just like any function in the Wolfram Language:
Here is an example of 128-bit integers. Note that these are not like big integers in the Wolfram Language, which are implemented in software, need a memory allocation and can resize accordingly. Such large integers have many uses:
For some functions, efficiencies can be determined at compilation time. Here, the negative function is invoked on an unsigned integer. It is known at compile time that the result is always false:
A key point is that when a Wolfram Language program uses functions such as Plus or Times, the compiler will select a specific implementation according to the types of the arguments.
Information on Types
A list of the major types used by the compiler can be found in the documentation for Compiled Types. Also, specific information can be found with CompilerInformation. For example, as in the following:
The output contains many hyperlinks to allow easy navigation, helping to get an overview of compiler types.
One detail about the way the Wolfram Compiler works with types is that they can be grouped with abstract types (or type classes). These are helpful for writing code that has a single function that works for different types. This is called polymorphic code and will be seen a lot in the Wolfram Compiler.
This displays information about the "Integers" abstract type class, including the types that implement it (i.e. the instances of the abstract type). For example, "Integer64" is an instance of "Integers":
There is more detailed information on CompilerInformation in a later section.
Floating-Point Types
The compiler also supports various floating-point types. The following function takes a 64-bit real number and returns the value of the Sin function for that input:
Some real computations have the possibility of domain errors. For example, the square root of a positive real number can be represented as a real number:
However, for negative inputs, a domain error is generated. The handling of the error is that the computation is rerun in the Wolfram Language interpreter and is described in a later section:

As with integers, there are a variety of different-sized floating-point types. This computation is done with a 16-bit real number:
CompilerInformation is useful for exploring the working of floating-point types:
The "FloatingPointReals" abstract class is a useful grouping of floating-point types:
Complex Types
A variety of complex types are supported, currently limited to those with floating-point real and imaginary parts:
Complex numbers are free of the domain errors that limit real functions:
The result is still defined for a negative input:
There is also a "ComplexReal32" type. This is a complex type where the real and imaginary parts are "Real32":
Validation
A key element of the Wolfram Compiler is that arithmetic functions are, by default, validated. They are set to track issues such as domain errors. This function takes a 64-bit integer and increments it:
If the result of the addition is outside of the range representable by a 64-bit integer, an error occurs. The Wolfram Language handles this by rerunning the computation directly in the evaluator. Since this operates on expressions, it returns a result, but not as a machine integer:

Similar issues apply to overflow of real numbers:

When a number gets too small, it returns a zero, and there is no error:
The behavior when there is an error can be modified by the CompilerRuntimeErrorAction option:
Now there is an error, but a Failure object is returned:

More information on error handling from FunctionCompile can be found in a later section.
Automatic Conversion
When functions such as Plus or Times are applied to arguments of different types, a conversion takes place. The conversion is to convert into a common type.
If the argument types are equal, the result has the same type:
If the argument types have different sizes, then they convert into the larger type:
If one argument is unsigned and the other signed, the result is unsigned:
If one argument is an integer and the other a real, the result is a real:
If one argument is complex, the result is a complex:
Converting between Numbers
Conversion between numbers can be carried out by certain standard Wolfram Language functions such as N or Floor:
However, explicit conversions can also convert between types with the Cast function:
You can choose a "CCast" conversion that is closer to the way that C converts between numbers:
When the number fits nicely, it works as expected:
When the number does not fit into the dynamic range of the output type, the result can seem strange:
Cast is a very general function that works for many types and will be seen many times.
Constants
When constants are used in compiled code, they are given the appropriate value but also given a type. Integer constants are given the machine integer type. On a 64-bit operating system, this will be a 64-bit integer:
Floating-point constants are given a 64-bit real type:
Complex constants are given a complex number with 64-bit real parts:
To make constants of different types, you can use TypeHint:
This can be important if you want to work with smaller integer types. The following adds a constant to an "UnsignedInteger8":
The result seems to be correct:
However, using Information to extract the return type, it can be seen that due to type conversion, the result is actually a 64-bit integer. This might not actually be desired:
In this example, the constant is set to be an "UnsignedInteger8" like the argument. This causes the return type of the compiled code to be an 8 bit unsigned integer:
Another way to write this would be to use TypeOf:
Note that even though the value is given as an integer zero the constant actually has the type given by the TypeHint. In this case a real zero is returned:
This generates a complex zero:
Being able to create constants for a variety of types by changing the argument of TypeHint will be very useful for writing polymorphic code.
Array Constants
There are various array constants that can be given. These include packed arrays:
Constants can be numeric arrays:
Constants can also be raw C arrays of data. In the following, a raw C array is created and one element is extracted:
More information on packed and numeric arrays, as well as C arrays, will be given in other sections of the documentation.
Mathematical Constants
Mathematical constants, such as Pi, are supported if they are used to create approximate numbers. For example, in the following:
However, they are not supported as entities themselves. Hence the following is an error:

The same applies when working with fractions. If they are used with a floating-point input, compilation can succeed:
However, they are not supported on their own, and the following cannot be compiled:

Similar issues apply to complex numbers. The following compiles fine:
However, this cannot be compiled. Complex numbers with integer elements are not supported:


The way to avoid these problems is to make sure that any of these constants are used in conjunction with floating-point variables. Another solution is to use TypeHint:
Equality Comparisons
The Wolfram Language provides two main functions for testing for equality: Equal and SameQ. These have input syntax of == and ===, respectively. They have different behaviors, and it is worth understanding the differences.
The following function takes two 64-bit integers and compares them with SameQ:
This function compares 64- and 32-bit integers. SameQ only returns true if both arguments have the same type and their values are the same. This means that this function that compares 64- and 32-bit integers will always return true. As a consequence, a warning is issued:

This also returns false even though the arguments look at the same. This is because in the compiled code they do not have the same type, and SameQ is always false for things of a different type:
It is important to know this when comparing constants. Working with a 64-bit integer is straightforward:
However when comparing a non-machine integer with a constant, the warning is generated. The comparison is aways false:

This is also false, because the constant is a 64-bit integer and SameQ is false for different types. A special warning is generated to indicate this:
One solution is to use a constant of the appropriate type:
TypeHint has a useful property in that it is stripped out in uncompiled code. This allows compiled and uncompiled code to give the same result:
Another solution is to use Equal:
Equal works by converting the arguments to a common type. This is convenient in this case, but introduces extra costs.
Rectangular Data
The Wolfram Compiler has various ways to work with data. These tend to follow the support in the Wolfram Language, which has particularly efficient support for working with rectangular arrays of raw numbers. A rectangular array is one where the length of every element at a given dimension has the same length. So rectangular arrays are good for representing matrices and vectors.
The Wolfram Compiler provides two rectangular types: "PackedArray" and "NumericArray". A related type is "ByteArray", which holds a vector of bytes.
Here are some examples of rectangular arrays. They use the TensorQ function, which returns True if the argument is rectangular. These help to give some flavor of the topic:
It is obvious that any vector is rectangular, but here is an example of an expression that is not rectangular:
Here are some other useful Wolfram Language functions that help to work with arrays:
Packed Arrays
Packed arrays are rectangular arrays that can contain a limited set of numeric types. They are represented by lists in the Wolfram Language. The following function takes a matrix of reals and returns the second row. The type used here is "PackedArray"::["Real64",2]:
"PackedArray" is a compound type, a type that takes arguments. The arguments are the type of the elements that it contains and its depth. In the case of a matrix, this is 2.
"CompilerInformation" returns useful information. It shows that it takes two type variables:
Information returns the type of the compiled function in a computable form:
If the input to a function taking a packed array is not rectangular an error results.

Packed arrays can be created by using a Wolfram Language list, {…}:
They can also be created with Table:
The Range function is a particularly convenient and efficient way to create a packed array:
Many mathematical functions for working with arrays and lists are supported:
Wolfram Language functions that return lists typically return packed arrays if possible. In this example, NestList returns a packed array:
ListVector
If the input to a list is not something that can be packed, a "ListVector" type is returned:
Information confirms the return type of the compiled function is a "ListVector":
When a "ListVector" instance is returned to interpreted code, it becomes a Wolfram Language list {…}:
"ListVector" instances are useful because they can store both general data and non-rectangular data.
NumericArray
An alternative rectangular array type is provided by "NumericArray". This has an advantage over a "PackedArray" in that it supports more types. It has a drawback in that it is not represented by a list in the Wolfram Language. This example takes a matrix of "Integer16" values:
To call the code, a numeric array object has to be created, like the following:
A list can also be passed to a compiled function that expects a numeric array:
The "PackedArray" type does not allow construction from as many argument types. Therefore the following is an error:

The advantages of "PackedArray" over "NumericArray" include broader compatibility with compiled code and a more convenient expression-based representation.
ByteArray
Another array type related to packed and numeric arrays is "ByteArray". By definition, it is trivially rectangular, as it only stores a flat vector of byte values (and has a depth of one):
A numeric array object to pass into the compiled code:
Lists
When a Wolfram Language list {…} is compiled, if the arguments have a type that is valid in a packed array, the result will be a packed array.
In this example, the arguments are both "Integer64", and the result is a packed array of the same with a rank of one:
Here the arguments are both packed arrays and the result is a packed array:
Information confirms the result is a "PackedArray" type (the rank has gone from 1 to 2):
Calling the function, if both inputs have the same length, the computation works:
When the lengths are different, a runtime error results:

If you want to make a collection that holds both of these packed arrays, you can use "ListVector". This can be done by using TypeHint. This directs the compiler to make a result of this type. Note how the type given to the TypeHint, "ListVector"::[_], uses a blank _. This is convenient because it does not specify the actual contents of the list vector:
Now the compiled function can return a result:
This is a different way to write the same thing; it is just less convenient:
Loop Structures
Loop constructs such as Do, Range and Table are supported. The latter two by default create packed arrays:
Do can also iterate over the elements of container structures, such as packed arrays:
This is a convenient, concise way that avoids a lot of extra code to extract elements. It is also useful for writing code that works for different types:
Table returns a packed array:
Range also returns a packed array:
Functional Constructs
The Wolfram Language supports a wide range of functional constructs, many of which also work in compiled code:
Many Wolfram Language commands that work with functions are supported in the compiler:
Commands such as AllTrue, which take a nested function, are much faster in compiled code:
Restrictions
Some features of Wolfram Language code do not carry over to compiled versions. These are explored in this section.
Unknown Symbols
Wolfram Language programs allow unknown symbols to be used without a problem. The following function assigns to the global symbol x, and executes without a problem:
However, this function cannot be compiled. Since the compiler does not know about x, it gives an error. In general, if the compiler does not know something, it will fail:

The solution is to use a scoping construct to localize the variable; this is both safer and better style:
Now the function can be compiled:
Other scoping constructs such as Block, With and Function are also supported in compiled code.
Sometimes, global symbols are used to make new data objects. In compiled code, this is typically done with a TypeDeclaration.
Branching Constructs
Branching constructs such as If, Which and Switch are also supported in the compiler.
If two branches have different types and are used, there is an error:

If the result of the conditional is not used, then there is no problem:
If a symbol is initialized in different ways in different branches, the code will not compile:

If a symbol is not certain to be initialized, the code will not compile:

If not all branches are covered and the result is used, the code will not compile:

Similar issues apply to other branching constructs. In this case, since t might be other than 1 or 2, compilation does not work:

If a catchall case is added, then compilation works:
These issues do require some extra work to avoid, but very often they are sources of bugs and unintended consequences. So the compiler is often helping you to create better code.