Compiled Program Features
Calling the Evaluator | Calling Libraries |
Input and Output | Raw Data |
Nested Functions | Measuring Timing |
Incremental Functions |
This section reviews various features of coding that are specific to the Wolfram Compiler. These are to be distinguished from general features of programming in the Wolfram Language supported by the compiler.
Calling the Evaluator
The Wolfram Compiler provides built-in definitions for many functions covering many types. However, it does not cover everything. The development team steadily expands the coverage, trying to ensure that any new functions have robust and efficient implementation. In addition, compiled code does not have coverage for third-party functions.
One way to extend the functionality of the compiler for which an implementation is not available is to use the evaluator. There are a variety of ways that this can be done, and they are explored in this section.
Here is an example of a function that does not compile. There is an error message:

You can confirm that it is not supported by using CompilerInformation:
There are various ways to add support for a function that is defined in the Wolfram Language. These are explored in this section.
KernelEvaluate
One way to do this is with KernelEvaluate. This needs TypeHint to describe the type of the result:
KernelFunction
Another way to call the Wolfram evaluator is to use KernelFunction. This has a convenient syntax for using in a declaration. Putting code into a declaration is useful because it can be added to a CompilerEnvironment and used in many places:
Note that a declaration could also be written using KernelEvaluate. It is just simpler to use KernelFunction.
InertEvaluate
It is also possible to call the Wolfram Language by using InertEvaluate; this was seen in the section on working with expressions. Generally, KernelEvaluate or KernelFunction is simpler to add calls to the evaluator, but InertEvaluate is shown here for completeness:
Note that all three versions all work in the same way, with KernelEvaluate and KernelFunction being implemented in terms of InertEvaluate.
Input and Output
The Wolfram Compiler offers native support for Print and Echo. These can be quite useful for tracking the progress of code. For example, progress through this loop can be printed:
Echo is also supported:
File Operations
The Wolfram Compiler does not provide any native support for Wolfram Language file operations. However, it is very straightforward to use the techniques for calling the Wolfram Language interpreter.
For example, to generate results and store them in a file, you can do something like the following:
If you want to read in data, you can do something such as the following:
This uses the file that was created earlier:
Nested Functions
In the Wolfram Language, functions can appear nested inside other functions being created in a light-weight fashion. There are several ways they can be entered into the system, with the following all creating a function that adds its arguments:
In the Wolfram Language, these are sometimes called anonymous functions. They can be passed around as data. It is quite common that they are passed as arguments to functions that need to execute something, such as Map, Fold or AnyTrue.
Nested functions are supported in the Wolfram Compiler. Typically, they do not need type annotations, since the types of their arguments are often determined by the type system. An example of the use of a function is this usage in Map:
There is nothing special about the syntax used to create the function. The example could be written as:
This runs the same as previously:
Functions can be passed as data, as in this example:
Functions can be passed to other functions (this is written with full Function syntax for clarity):
Functions can be returned from other functions (currently this requires a TypeHint to assist the type-inferencing system):
Captured Variables
Nested functions can also capture variables from the code in which they appear. In this example, the nested function captures an argument, arg2, of the outer function:
This can be very useful, since it allows extra arguments to be passed into a function that has a fixed signature. For example, the following uses Map, but it wants to pass in an extra argument to add to the elements of the list. This could not be done as an argument to the function passed into Map, since that has a signature that was already set. Using a captured variable solves the problem:
Nested functions that capture variables can be passed as arguments to other functions:
However, there are some limitations in the current implementation of nested functions with captured variables.
Captured variables cannot be updated:

Function that capture variables cannot be returned from other functions:

Capturing of scoped variables may seem like a rather arcane feature, but it is actually quite common usage. It is intended to remove these limitations in a future version of the Wolfram Compiler.
If an update to a captured variable is needed, this can be done by passing in something such as a "Value" data structure or a "RawPointer". An example of the latter is shown later.
Declarations with Nested Functions
To write a declaration that uses a nested function requires the use of function type syntax {args…}res. An example of this is shown below. This is quite similar to the AnyTrue function but shows how declarations that take nested functions can work. Note how the input type of the nested function matches what is stored in the packed array:
This is an example of using the declaration:
This calls the function; everything is greater than zero, so the result is False:
Here there is an argument less than zero, and the result is True:
Another use of the same declaration passes in a different function and works for a packed array with complex arguments:
This gives the expected result:
The declaration will also work for a nested function that uses a capture. This shows the value of captures; the nested function can be changed, but there is no need to recompile the function:
At least one of the arguments is less than 10, so the result is True:
None of the arguments is less than 0, and hence the result is False:
Incremental Functions
Incremental functions allow their execution to be paused and resumed multiple times. Each time the incremental function is paused, all of the local variables are saved; when the function is resumed, the local variables are restored and execution starts from the point where it had been paused. This is in contrast to functions that once called, execute their instructions and run to completion. Incremental functions lead to a number of interesting possibilities and are nicely integrated in the Wolfram Compiler.
A function is marked as being incremental by adding IncrementalFunction as in the following example that creates compiled code to return an incremental function:
The compiled code returns an "IncrementalFunction" type. The type has two arguments; in this example they are "Integer64" and "Null". The first argument is "Integer64" because this is the type of value that is yielded by IncrementalYield:
When the compiled function is called, it returns a data structure that is a handle to the running instance of the incremental function:
Information shows the operations of the incremental function:
When the "Next" operation is called, this causes the incremental function to run to the yield point. Here the function pauses, returning the argument of the IncrementalYield. At this point, the incremental function is not finished; instead it is paused:
When the "Next" operation is called again, the incremental function resumes. It continues execution until it reaches the next yield and returns that value. Again, at this point, the incremental function is not finished; it is paused:
Calling "Next" a third time causes the incremental function to resume. This time it does not find a yield; it reaches the end and a Failure is returned. This is because there is no value to be returned from the operation:
Once an incremental function has terminated, it cannot be resumed. Note that each time the compiled code is called, it makes a new instance of an incremental function.
There are a number of things you can do with incremental functions. For example, you can call an incremental function from a Do loop. This is done here with a new incremental function instance:
You can also use an incremental function in Table:
It is often useful for an incremental function to contain a loop. This incremental function takes a number as input and then yields a sequence of multiples of that number:
Create the function and get the next value:
You can call the elements from Table. This needs care because this incremental function continues forever, hence the example here has a limit on the number of elements in the table:
A different initial value causes a different sequence to be generated:
Incremental Receive
Incremental functions can generate values and then yield them. An alternative way to use them is to have them receive values at the suspend points. This is done with IncrementalReceive, as in the following example:
The compiled code return type is an "IncrementalFunction" with two arguments: "Null" and "Integer64". The first argument is "Null" because there are no yield points and nothing is yielded. The second argument is "Integer64" because this is the type of value that is received. This type is set by the argument of the IncrementalReceive, which gives a default value:
When the compiled function is called, it returns a data structure that is a handle to the running instance of the incremental function:
The incremental function pauses before any code has been executed. In order to reach the first suspend point, a call to "Next" is required:
Now "Send" can be called with an argument. This becomes the return value of the IncrementalReceive. When a value is sent, the function resumes execution, with the value being the result of the receive. This is passed into Echo. Execution then continues to the next receive point, where the function pauses:
The function can be sent another value. It will echo this and continue to the end of the function:
Now the incremental function has finished, and sending it another value will return a failure:
Here is another example. A new instance of the incremental function is created. It runs to the first receive with a call to "Next":
Now "Next" is called. The incremental function is paused at a receive and is thus expecting a value. However, a value was not sent. In this case, the default value, which was 1, is returned from the receive. This is echoed, and execution proceeds to the next receive:
Now sending a value works as expected:
The incremental function has reached the end, so it cannot be resumed again:
In Compiled Code
Incremental functions can be returned from compiled code directly to the Wolfram evaluator. In this case, they can be interacted with like other data structures in the Wolfram Languages. They can also be used in compiled code. Typically, in this case a function declaration is made.
This is a declaration of the code that yields a sequence of multiples. It has been made to be polymorphic in the type that is yielded. It has a limit to stop it from continuing without bound:
This compiles a function that uses the incremental function:
Note that the type of the compiled code is not to return an incremental function but to return a result computed using the incremental function:
The incremental function is created internally in compiled code and runs immediately. It echoes the incremental values and returns the number of times it was called:
Here is another call to the incremental function code. It uses different types, in this case "Real64", but the declaration works because it was polymorphic:
Expressions
Incremental functions are a powerful feature, but they are not supported for general interpreted Wolfram Language code. The code always has to be compiled.
However, the compiled code can involve Wolfram Language expressions. As has been seen, the Wolfram Compiler can work with expressions, and these can be used in incremental functions.
The example uses an example file of numbers:
This example returns an incremental function that holds a stream expression to a file and a format. It then reads from the stream until everything has been read:
The compiled code returns an incremental function that yields an "InertExpression":
This calls the compiled code and gets an incremental function that can return the results:
The format that was passed was for single numbers, and this is what "Next" returns:
You can read from the current point to the end of the file:
This reads numbers three at a time:
The file is exhausted, and there is nothing to be yielded:
An incremental function can be created with a file that does not exist:
The first call to "Next" fails because the file cannot be opened. A failure is returned:

Recursive Incremental Functions
Incremental functions can call themselves recursively, which leads to many interesting possibilities. One example is traversing a data structure, which is easily written in terms of a recursive algorithm.
This is shown in this example, which will mimic the Wolfram Language function Flatten:
This is the incremental function declaration. The input is yielded if it is an atom. If not, the elements of the expression are extracted with a Do loop; they are used to create a new incremental function, and the results of this are collected:
Here an instance of the incremental function is generated with an expression input:
The elements of the expression can be retrieved with "Next":
For another example, here is a nested list structure:
The elements can be retrieved one at a time and added up with Sum:
An alternative way to sum the elements would be to flatten the structure and use Total:
The incremental function here is a particularly simple way to extract the elements and then do something with them. It has an advantage over the approach using Flatten and Total, since that way will require a lot of data copying. Another possible way would be to recursively descend the expression, applying some nested function, but that would be quite a lot of coding.
Calling Libraries
The Wolfram Compiler provides useful functionality for calling code from external libraries, especially those that have a C-compatible interface.
A simple example uses a sample library that is shipped with the Wolfram Language for demonstration purposes. It makes a LibraryFunctionDeclaration for the function in the library to be called:
This compiles a function that calls into the library:
There is good documentation on the subject of calling external libraries with the compiler, and the reader should study this. Another useful resource is the documentation on the foreign function interface. This has a useful section on how libraries are found, which is also relevant to uses of the Wolfram Compiler for working with libraries.
Raw Data
The Wolfram Compiler provides types to hold raw data. "CArray" is a parametrized type to hold repeated elements of the same type in an array. "RawPointer" is a type to hold a single element of a type. In addition, there is an unmanaged type "OpaqueRawPointer". "CArray" and "RawPointer" are somewhat similar to the type t* in C: a pointer to a type t. "OpaqueRawPointer" is somewhat similar to the type void* in C: a generic pointer to something.
"CArray" and "RawPointer" have already been discussed in the section on memory management. As was pointed out there, they do not have any automatic memory management and so require manual collection. As was discussed, the "Managed" type can be useful to obtain automatic collection of these types. In addition, it discussed how CreateTypeInstance and DeleteObject could be used to create and free memory for these types, and that ToRawPointer could create an instance of "RawPointer" on the program stack that would not need freeing when the function exited.
This section will discuss a little more about the use of these types.
RawPointer
Raw pointers can be used to pass an address into a function, for example, to store results.
An example can demonstrate this. In this declaration, a pointer is filled in with a value if a condition is met. The function returns a Boolean indicating whether the pointer was updated:
Here the declaration is called. If the pointer was filled in, the value is extracted and returned, otherwise, -1 is returned:
The pointer is updated and the new result returned:
Another example is using a pointer to work around a weakness of the nested function support. As was described earlier, captured variables cannot be updated in a nested function. However, if a pointer is used as a capture, then a new value can be stored.
This is shown in the following. It uses Scan to iterate over the elements in a packed array. The function passed into Scan updates a value stored in a pointer by adding in the element:
This runs the function, returning the expected result:
It should be noted that this is an example of how to use a raw pointer with a closure to store values and work around a weakness in nested function support. In fact, when this weakness is resolved, it will actually resolve it by doing something very much like the support shown here.
This is not a good way to actually sum the elements in an array. If that was the goal, then it would be better to use Total. If the goal was to pass through a structure accumulating a result, then Fold might be preferable.
In addition, it should be noted that the technique of using a "RawPointer" will work best if the value stored is raw data such as an integer. If it is something that might require memory management, it might be better to pass in a "Value" struct.
Measuring Timing
The Wolfram Language provides several functions for measuring timing of functions. Key functions are AbsoluteTiming and RepeatedTiming. You can also run these in compiled code, and sometimes this can be quite useful. Of course, on any system, the operating system always makes timing measurements somewhat difficult because other processes are running.
Here is a function declaration to test the timings:
AbsoluteTiming will measure the time for one run of the function (the time is returned first). It is doing 100000 repetitions:
RepeatedTiming will measure the time for many runs of the function (the time is returned first):
This is a compiled function, but now AbsoluteTiming is inside the compiled code:
This is a compiled function, but now RepeatedTiming is inside the compiled code:
So the timing for all of these is quite consistent. However, where it can start to diverge is for much faster timings. Now it will do 10 repetitions.
The first two results have the timing outside of the compilation:
These two carry out the timing inside the compilation:
It is quite common that at the top level, repeated timing is faster than absolute timing. Another thing is that these are measuring the time in the compiled code but also the time to reach the compiled code. This has to go through various steps from the Wolfram Language evaluator, such as carrying out argument deserialization, and these take time.
The two timings for measurements inside the compiled code are much faster. The absolute timing can be 0 like here; this is sometimes an artifact of the measurement. The repeated timing carries out the computation several times and is probably the most accurate, since it is taking an average. Both of these measurements benefit from not measuring any overhead of the Wolfram Language evaluator. Probably the repeated timing is giving the best measurement of the actual speed of the operation. This is particularly useful if you want to run fast computations.