Table of Contents
Last update: 09.05.2026.
Some of the material has been collected from the Wafl Project source files. These files document some features that have not yet been prepared in regular tutorial form. Please forgive us for some technical inconsistencies in these files.
Wafl Evaluator provides a basic support for Just In Time compilation. Wafl JIT optimization is based on using the extern binary libraries. The interpreter first analyzes the code and selects the functions that can translate to C++. Then it generates the C++ source file and builds the binary library. The built library is dynamically loaded and linked. The linking process effectively replaces the usual Wafl evaluator nodes with dynamically loaded library nodes.
To use JIT optimization, the user/programmer needs to:
JIT optimization can be used it two different phases and with some significant differences in the algorithm and the capabilities: on abstract syntax tree (AST) and on abstract evaluation graph (AEG). It cannot be used in both ways at the same time. The default behavior is to have the JIT optimization on the AEG.
By default, the JIT optimization is disabled. To enable JIT, one of the JIT-related command line options must be specified.
-jit-ast - Enable JIT optimization on
AST;-jit-aeg - Enable JIT optimization on
AEG;-jit - Enable default JIT optimization (AEG in
current version);-jit-rebuild - Enable default JIT optimization
and force a module rebuild.The build process is based on external compilation tools and the Wafl Core library. To set up the build configuration, use Wafl’s ini configuration files. The ini configuration is obtained by reading and merging three configuration files, so that the contents of the second override the contents of the third, and the contents of the first override the contents of both the second and the third.
.wafl.ini in the current directory;.wafl.ini in the user’s home directory; andwafl.ini in the global program configuration directory
(/etc for Linux or %PROGRAMDATA% for
Windows).For Linux, it is assumed that c++ tools are used
(g++ or clang can be set). The JIT
configuration options are located in the [jit-g++] section
of the ini file. The default configuration is expected to work
for Linux, so in most cases this section can be left blank.
All the paths are specified without quotes.
It is possible to have multiple versions of the Wafl Core
libraries running at the same time. They are usually all located in the
/usr/lib/wafl/dev/ directory, and are named like
GNU_15.2.0_uxmkfl_x86_64_8_1_CPP20_Release. Each of these
versions contains include and lib
subdirectories, with the corresponding headers and libraries.
The latest Wafl installation sets a symbolic link
/usr/lib/wafl/dev/default to the latest installed Wafl
Core library. However, if an older version, or a version located
elsewhere, must be used, then it must be configured in the ini
files by setting the value of the wafl-core-path variable
in the [jit-g++] section:
wafl-core-path = ~/waflcore/GNU_15.2.0_uxmkfl_x86_64_8_1_CPP20_ReleaseThe corresponding include and lib
subdirectories will be automatically used.
Include Search Path contains a list of directories where C++
header files are located. It must include all compiler libraries, OS
system libraries and Wafl Core library. The include files
search path is specified by using multiple lines with variables named
like include-path-..., or by using a single variable
include-path with directories separated by ;.
For example:
include-path-1 = ~/dev/somelibs/includeThese parameters rarely need to be set manually. If the C++ development tools are configured correctly, and the path to the Wafl Core library is set correctly (or the default version is used), then the JIT should not use any other include directory.
The same applies to Libraries Paths as to Include Paths. The only
difference is that the variables are named
lib-path-....
Compiler options are specified using multiple lines with variables
named like compiler-options-..., or using a single variable
compiler-options. The options are separated by spaces.
There are no different configurations for debug and release builds. The
same built JIT modules can be used by the Wafl interpreter for
both debug and release mode.
For example:
compiler-options-1 = -std=c++20 -fpic -ldl -shared
compiler-options-release = -O3 -DNDEBUG
#compiler-options-release = -save-tempsPlease do not change compiler options unless you are absolutely sure you know what you are doing.
The same applies to linker options as to compiler options. The only
difference is that the variables are named
link-options-....
After the JIT build process is complete, the generated C++
source files and command scripts are deleted. If you need to keep the
source files in the user’s TEMP directory, use the
keep-source=1 configuration option.
For Windows operating system it is assumed the MSVC tools are used.
JIT configuration options are located in
[jit-msvc] section. If paths to the MSVC tools are added to
the system paths, then the default configuration is expected to work,
and this section can be left blank.
All the paths are specified without quotes.
The easiest way to set all the paths is to specify the path to the
script that sets all the paths, as a value of the variable
msvc-config-bat. For example, for Visual Studio 2022 that
should usually be:
msvc-config-bat = C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsall.batThis script will be run, to set all the other MSVC paths (tools, include and lib).
An alternative way to set the path to the compiler tools is to
explicitly set the value of the tools-path variable. In
that case, the include and lib directories will usually need to be set
manually.
It is possible to have multiple versions of the Wafl Core
libraries running at the same time. They are usually all located in the
...\Wafl\dev\ directory, and are named like
MSVC_143_win64_AMD64_8_1_CPP20_Release. Each of these
versions contains include and lib
subdirectories, with the corresponding headers and libraries.
The latest Wafl installation sets a directory junction link
...\Wafl\dev\default to the latest installed Wafl
Core library. However, if an older version, or a version located
elsewhere, must be used, then it must be configured in the ini
files by setting the value of the wafl-core-path variable
in the [jit-msvc] section:
wafl-core-path = C:\WaflCoreLibs\GNU_15.2.0_uxmkfl_x86_64_8_1_CPP20_ReleaseThe corresponding include and lib
subdirectories will be automatically used.
Include Search Path contains a list of directories where C++
header files are located. It must include all compiler libraries, OS
system libraries and Wafl Core library. The include files
search path is specified by using multiple lines with variables named
like include-path-..., or by using a single variable
include-path with directories separated by ;.
For example:
include-path-1 = C:\app\somelibs\includeThese parameters rarely need to be set manually. If the
msvc-config-bat is configured correctly, and the path to
the Wafl Core library is set correctly (or the default version
is used), then the JIT should not use any other include
directory.
The same applies to Libraries Paths as to Include Paths. The only
difference is that the variables are named
lib-path-....
Note: Windows SDK libraries are required. If they
are installed, the script configured in msvc-config-bat
should add them to the paths automatically.
Compiler options are specified using multiple lines with variables
named like compiler-options-..., or using a single variable
compiler-options. The options are separated by spaces.
There are no different configurations for debug and release builds. The
same built JIT modules can be used by the Wafl interpreter for
both debug and release mode.
Please do not change compiler options unless you are absolutely sure you know what you are doing.
The same applies to linker options as to compiler options. The only
difference is that the variables are named
link-options-....
After the JIT build process is complete, the generated C++
source files and command scripts are deleted. If you need to keep the
source files in the user’s TEMP directory, use the
keep-source=1 configuration option.
Here is a complete example, for VS 2022, Windows 11 and appropriate Wafl Core:
[jit-msvc]
msvc-config-bat = C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsall.bat
wafl-core-path = C:\Program Files\Wafl\dev\MSVC_143_win64_AMD64_8_1_CPP20_ReleaseWafl Binary Libraries are function libraries
developed in C++. The main support for implementation of Wafl Binary
Libraries is the libExtern library.
This document describes the libExtern library and the
Wafl Binary Library development process. libExtern is pure
header library. It consists of two header files:
ExternLibInterface.h, which defines a function
arguments marshalling interface, andExternLibBase.h, which defines the library catalog
interface and the catalog object itself.Each C++ source file implementing a binary library should include the first header, but only one source file should and must include the second.
The library loading is implemented in
BinaryLibraryDefinition class and method
ImportLibrary.
Based on the libExtern library, the
WaflCore project is exported as waflcore
library. It includes all elements of the Wafl project that are required
to build binary libraries out of the main Wafl project.
The usage of the waflcore exported library is similar to
the usage of the libExtern library. It consists of:
include/waflcore/libExtern.h is a header file,
equivalent to ExternLibInterface.h. Ito should be included
by each module that implements some parts of the binary library
interface;include/waflcore/libExternBase.h is a header file
equivalent to ExternLibBase.h. It must be included by one
and only one module, because it defines some interface objects;lib/libWaflCore.lib or lib/libWaflCore.a
(depending on the platform) is a static library that contains all
required elements of the Wafl project.Binary defined libraries are used in a similar way like the common
Wafl defined libraries. To explicitly declare a binary function, the
extern prefix is used:
myLib = library extern '<library file name>';The library filename is usually specified without any extension. Each
implementation will automatically add the appropriate extension
(.dll for Windows and .so for Linux) when
loading the library. Moreover, if the library is not found, the
interpreter will try to add a prefix libw to the library
name. Also, if prefix extern is not specified, the library
manager will first try to load a binary library, and if not found, then
it will try to load a regular Wafl library.
For example, the library defined in a file libwRegex.so
can be declared as:
regex = library 'Regex';The library functions are accessed as usual, using ::
domain operator:
... myLib::fn1 ...
... myLib::fn2 ...To list the content of a binary library, the usual command line interface can be used. Because a large number of extern libraries may exist, the extern library content is listed only if a library name is used as a filter.
For example, to list the contents of a binary library
libwRegex.so, one of the following commands can be
used:
clwafl ? Regex
clwafl ? libwRegex
clwafl ? libwRegex.soFor more detailed descriptions, use ??:
clwafl ?? RegexThe import syntax is also available.
Each exported binary function have to have a so called canonical form:
void aFunction( EvCell* reserved, const EvCell* args )The first argument reserved is pointer to the
preallocated (non-constructed) space where a result will be stored. The
second argument args points to an array of function
arguments. All arguments and result are objects of EvCell
type.
It is, of course, usual to define (or to already have) a function in a regular way, like:
int fn( int, float, bool )or like methods in some of the defined classes, like:
int Type::fn( ... )The corresponding canonical function can be defined explicitly or automatically. If the canonical function is expected to be built automatically, then some of the following sections are not important. However, if the canonical function is defined explicitly, then it must follow some rules. The rules are explained in the following sections.
This section is not important if canonical functions are built automatically.
Each canonical function implementation has to do three jobs:
EvCell objects;EvCell object.The conversion of arguments and result is covered in the following
sections. It is not too complex, but for the most of the cases it
follows the same rules. This is why a wrapper function
wrappedFn is provided (as well as a wrapper class
FunctionWrapper), to make the wrapping of a
regular function easier.
For example, the following expression defines a canonical version of
library function for a given regular function fn:
wrappedFn< decltype(fn), fn >The wrapper function has the appropriate canonical type. It marshalls
all the arguments to the regular function, invokes the function and
finally marshalls the result back from a regular form to the
EvCell form.
The wrapper function (and the wrapper class) works for functions with
argument types supported by getArg template function and
result type supported by setResult template function. For
other types these template functions have to be specialized, first. See
the following sections for details.
This section is not important if canonical functions are built automatically.
Please consult the section on the supported types.
Each argument is read from the appropriate EvCell object
and marshalled to the appropriate expected type. It can be done manually
or using function template:
T getArg<T>( const EvCell* arg )
The template is defined for the common types as follows:
T (including bool):
(T) arg->AsInteger()int:
(int) arg->AsInteger()T:
(T) arg->AsFloat()double:
(double) arg->AsFloat()char* and const char*:
arg->AsString().c_str()std::string and std::string&:
arg->AsString().asBaseString()sml::String and sml::String&:
arg->AsString()T*, where T is a library defined
class, which is exported from the library:
(T*) TheLibrary.ParentContext() .GetObjectFromExternalObjectCell( arg )T, where T is an internal library
defined class, which is not exported from the library, the marshalling
uses Wafl records.To support another type, it is required to define the appropriate
specialization of the template function getArg. For
example, to support the type A, a specialization like this
is required:
template<>
A getArg<A>( const EvCell* arg )
{
...
return (A) ...;
}The new specializations will be used by the wrappers, too.
This section is not important if canonical functions are built automatically.
Function result has to be converted to Wafl EvCell
objects. It can be done manually or using function template:
void setResult<T>( EvCell* reserved, T value )The setResult is defined for the common types based on
the CellFactory functions (and wrappers), which are
available from the callers context. The supported types include integral
types, bool, floating point types, string types
(char*, std::string, sml::String)
and object pointers.
For example, integer results are marshalled like this:
TheLibrary.ParentContext()
.CreateConstIntegerCellAt( reserved, (fInteger) value );To support another type, it is required to define the appropriate
specialization of the template function setResult. For
example, to support a type A, a specialization like this is
required:
template<>
void setResult<A>( EvCell* reserved, A value )
{
... create a cell at reserved ...
... with given value of type A ...
}The new specializations will be used by the wrappers, too.
This section is not important if canonical functions are built automatically.
When registering a function, its Wafl type is specified using a type
string representation. For example, a function that maps an integer to a
float has a type ( Int -> Float ). For more details on
the types, see Wafl documents.
A function template is provided to automatically generate the type strings:
const fString& waflFnTypeName<FN>()It creates a function type string using another template function to get individual argument or result type name:
const char* waflSimpleTypeName<T>()Template function waflSimpleTypeName supports the common
types (just like getArg and setResult) and
requires further specializations for advanced cases.
All primitive types are supported:
Int type size corresponds to build settings and
ISA. For 64-bit processors it is signed 64 bit integer. However, any
integer type is allowed, but take care of the conversions;Float type is defined as C++ double
type. However, any float type may be used, but take care of the
conversions;Bool type corresponds to C++ bool
type.NOTE: If the canonical versions of the functions and/or the function types are automatically created, then the following additional constraints hold:
fBool is used as a logical type, almost equivalent
to the bool type. However, this type is defined as a 64-bit
unsigned integer. If integers are used, please take care not to use
64-bit unsigned integers (including size_t and similar
types) for arguments and results, because they may be miss-detected as a
logical type.
fInteger type represents 64-bit signed
integer.Wafl String type is internally defined as
fString, which is a synonym for sml::String.
They are both defined in the specified header files. Of course,
std::string is supported, also.
Because of the different handling of memory allocation in dynamic
modules in some environments (in Windows, for example, each DLL may have
its own memory heap), the String arguments and results have
to be freed in the same module where they are created.
String as argumentSo, the String arguments must not be freed in binary
function. Moreover, they must not be copied. Please copy only the binary
char* representation. This is why String&
is better to be used than simple String.
TO DO! The solution is modified and the following sentence is false. Check and improve!
However, if there is a specific case when a String
argument has to be destructed, then first the
void FreeString() const method must be invoked on the
string.
String as resultUsing String as result is similar to the argument case.
The string itself should be copied. This is why it is better to use
char* as result than String.
The following array types are supported:
Array[Int] corresponds to C++ type
fArrayInt;Array[Float] corresponds to C++ type
fArrayFloat;Array[Bool] corresponds to C++ type
fArrayBool;Array[String] corresponds to C++ type
fArrayString.Array[Array[Int]] corresponds to C++ type
fArrayInt2;Array[Array[Array[Int]]] corresponds to C++
type fArrayInt3;Float, Bool and
String.Types fArrayInt, fArrayFloat,
fArrayBool and fArrayString are interfaces to
Wafl arrays.
Arguments of array types must be specified as const
pointers, like:
size_t getArrayLen( const fArrayInt* arr )Such arguments must not be deleted by library functions.
Array must be returned by pointer. It will be deleted by Wafl evaluator after usage.
fArrayInt* getRandomArray( int range, int size )See examples for more details.
Array[...] corresponds to C++ type
fArrayOfCells. It is not recognized automatically and has
to be specified manually.Records are mapped to C++ classes defined in the library. Such classes are not exported from the library.
As C++ has no reflection features, it is not possible to implement
complete marshalling of record/class types automatically. The developers
have to provide explicitly the mapping of the types. The mapping is
specified by implementing a specialization of the template
RecordMarshalling.
To map a class MyClass to a Wafl record type, there are
a few requirements to fulfill:
template<> class RecordMarshalling<MyClass>
with four static methods has to be implementedMyClass WaflToCpp( const WAFL_ExternLib::Record* record )
for conversion of arguments of a Wafl record type to objects of the
MyClass class.
MyClass.MyClass should support a move
construction. static MyClass WaflToCpp( const WAFL_ExternLib::Record* record )
{
MyClass obj {
getArg_safe<int>( record->ElementByName( "intAttr1" )),
getArg_safe<int>( record->ElementByName( "intAttr2" ))
};
return obj;
}void CppToWafl( WAFL_ExternLib::Record* record, const MyClass& obj )
for conversion of function results of the MyClass type to a
Wafl record type.
record and with the appropriate attribute names.MyClass argument is
destroyed. static void CppToWafl( WAFL_ExternLib::Record* record, const MyClass& obj )
{
TheLibrary.ParentContext().CreateDynamicRecordAt( record, getRecordAttributeNames() );
TheLibrary.ParentContext().CreateConstIntegerCellAt( record->Elements(), obj.intAttr1 );
TheLibrary.ParentContext().CreateConstIntegerCellAt( record->Elements() + 1, obj.intAttr2 );
}3a. In some cases, if the object can be moved to a record, then: *
void CppToWafl( WAFL_ExternLib::Record* record, MyClass&& obj )
method should be implemented in a similar way; *
void CppToWafl( WAFL_ExternLib::Record* record, const MyClass& obj )
can be implemented to use the other one, like this:
static void CppToWafl( WAFL_ExternLib::Record* record, const MyClass& obj )
{
MyClass tmp = obj;
CppToWafl( record, std::move(tmp) );
}const char* WaflTypeName() returns a textual
specification of the corresponding Wafl record type. The usual form
is: static const char* WaflTypeName() {
return "Record[ intAttr2: Int, intAttr1: Int ]";
}const WAFL_ExternLib::RecordAttributes& getRecordAttributeNames()
returns an attribute names handler object. The name specified should be
ordered lexically. The usual form is: static const WAFL_ExternLib::RecordAttributes& getRecordAttributeNames()
{
static WAFL_ExternLib::RecordAttributes names {
"intAttr1",
"intAttr2",
};
return names;
}MyClass arguments
must be passed by value or by r-value reference
MyClass&&. The l-value references are not
allowed.MyClass results
must be passed by value.If all the requirements are fulfilled, the functions using
MyClass type as an argument or as a result are defined and
registered in the same way as any other function.
See examples in testLibExtern project for an
example.
Assume that the arguments will be deleted by caller. If the argument is to be returned, it must be copied.
Assume that the returned object will be deleted by caller after the usage. If an argument is to be returned, or some permanent object, then it must be copied.
Any class defined in the binary library may be exported as a
binary type. The only requirement is that it has to be
described by a corresponding descriptor object in the library object.
The descriptor object is created using
LibraryClassDescription template class in a form:
LibraryClassDescription< aType > aTypeDesc( "aTypeName" );When a function result is a binary type, the result is prepared using
SetResult method in the following form:
data.SetResult( new aType(...), aTypeDesc.GetData() );Please note:
new operator. It will be deleted by Wafl evaluator using
appropriate destructors and deallocators specified by the provided
descriptor.The only required method of the binary type class is
fString print() const method, which is used to create a
display-ready string representation of the object.
Each binary library must define a catalog of its content. The catalog informs the Wafl evaluator, which uses the library, on the function names, types and implementations.
The catalog has to be implemented by
LibraryImplementation::InitLibrary method of a predefined
class LibraryImplementation. The class and its singleton
object are defined in ExternLibBase.h header file, and the
initialization method is implemented by the library.
The InitLibrary method is invoked on an already created
LibraryImplementation object and it should populate it with
the functions data. The concept is simple and based on idea that each
chunk of information is inserted into the catalog using insertion
operator <<:
const char* : *this << "A Binary Library"VersionNumber object: *this << VersionNumber( 0, 6, 5 )LibraryClassDescription<...> object: *this << aBinaryTypeDesctLibFnData object. It can be exported in its
canonical form: *this << tLibFnData {
"maxInt", // exported fn. name
canonicalMaxIntFn, // canonical fn. implementation
"( Integer * Integer -> Integer )", // fn. type
"Returns a greater of two numbers." // fn. description
}It is easier to use macro LIB_FN_DATA, which creates the
canonical version of the function on the fly, and prepares its
registration data:
*this << LIB_FN_DATA(
"maxInt", // exported fn. name
regularMaxIntFn, // regular fn. implementation
"( Integer * Integer -> Integer )", // fn. type, using Wafl notation
"Returns a greater of two numbers." // fn. description
}There is also even more advanced macro LIB_FN_DATA_T. It
does all the same like the previous one, but also automatically
generates the function type name:
*this << LIB_FN_DATA_T(
"maxInt", // exported fn. name
regularMaxIntFn, // regular fn. implementation
"Returns a greater of two numbers." // fn. description
} *this << tLibFnData {
"aMethod", // exported fn. name
canonicalMethodFn, // canonical fn. implementation
"( TypeName -> Integer )", // fn. type
"Maps object to integer" // fn. description
}There are macros to automatically create canonical functions for
methods. The first one is LIB_METHOD_DATA, which creates
the canonical function for the method on the fly, and prepares its
registration data:
*this << LIB_METHOD_DATA(
"aMethod", // exported fn. name
TT::method, // regular fn. implementation
"( TypeName -> Integer )", // fn. type
"Maps object to integer" // fn. description
}There is also even more advanced macro
LIB_METHOD_DATA_T. It does all the same like the previous
one, but also automatically generates the function type name:
*this << LIB_METHOD_DATA_T(
"aMethod", // exported fn. name
TT::method, // regular fn. implementation
"Maps object to integer" // fn. description
}Note: Static methods are registered as functions, not as methods.
Please see the examples for more details.
A binary library must include libExtern/ExternLibBase.h
from Wafl project. This header file defines all the library elements
described in this file.
A binary library must link libExtern library from Wafl
project. This library already includes the other required Wafl
libraries.
Here we present some simple examples. For more information please see
the source for testLibExtern project and for
libw* sub-projects.
As a first example, we will export a sqrt function. The
first option is to define a canonical function:
void sqrtFnC( EvCell* reserved, const EvCell* args )
{
auto result = sqrt( getArg<double>( &args[ 0 ] ) );
setResult( reserved, result );
}This function uses the predefined marshalling template functions
getArg and setResult. It may be registered
using:
*this << tLibFnData {
"sqrt", // exported fn. name
sqrtFnC, // canonical fn. implementation
"( Float -> Float )", // fn. type
"Returns a square root of the number."
}The easier option is to use generic canonical functions, defined by
wrappers. However, because the sqrt is overloaded, we
should be more explicit on the version, like in:
inline double sqrtFn( double x ) { return sqrt( x ); }
auto sqrtFnC = wrappedFn< decltype(sqrtFn), sqrtFn >;It is easier to use some macros, which do some of the preparations
“on the fly”. The basic macro is LIB_FN_DATA, which creates
the canonical version and prepares it registration data:
inline double sqrtFn( double x ) { return sqrt( x ); }
...
*this << LIB_FN_DATA(
"sqrt",
sqrtFn,
"( Float -> Float )",
"Returns a square root of the number."
}Even more advanced is macro LIB_FN_DATA_T. It does all
the same like the previous one, but also automatically generates the
function type name:
inline double sqrtFn( double x ) { return sqrt( x ); }
...
*this << LIB_FN_DATA_T(
"sqrt",
sqrtFn,
"Returns a square root of the number."
}…
…
This function creates and returns as a result a new binary type object:
class aBinaryType {
public:
aBinaryType( int n ) : Attr_( n ) {...}
~aBinaryType() {...}
int Attr_;
};
LibraryClassDescription<aBinaryType> aBinaryTypeDesc( "aBinaryType" );
aBinaryType* createBinaryObject( int x )
{
return new aBinaryType( x );
}The function uses an object of a binary type as an argument:
int useBinaryObject( const aBinaryType* obj )
{
return obj->Attr_;
}The function computes the sum of integer array elements:
fInteger getArraySum( const fArrayInt* arr )
{
fInteger sum = 0;
for( auto i : *arr )
sum += i;
return sum;
}The life of array argument object is managed by Wafl evaluator. If a pointer to the object is is stored internally by a library, then the evaluator must be informed of that:
arr->AddReference();When such rerefence is deleted, teh evaluator must be informed of that, also:
arr->Free();The function creates and returns an array of size random
integers in the range [0,range-1]:
fArrayInt* getRandomArray( int range, int size )
{
fArrayInt* arr = (fArrayInt*) TheLibrary.ParentContext().CreateUninitializedArrayCellPtr( size );
auto set = TheLibrary.ParentContext().CreateConstIntegerCellAt;
for( auto& i : *arr )
set( arr->getElementAddr( i ), rand() % range );
return arr;
}The library defined in previous examples may have the following catalog:
...
void LibraryImplementation::InitLibrary()
{
*this
<< "A Binary Library" // Library name
<< aBinaryTypeDesc // A binary type
// Functions...
<< tLibFnData{ "sqrt1", sqrtFn,
"( Float -> Float )" }
<< tLibFnData{ "maxInt", maxIntFn,
"( Integer * Integer -> Integer )" }
<< tLibFnData{ "stringAdd", stringAddFn,
"( String * String -> String )" }
<< tLibFnData{ "newBin", createBinaryObject",
"( Integer -> aBinaryType )" }
<< tLibFnData{ "useBin", useBinaryObject,
"( aBinaryType -> Integer )" };
}