Target languages#

Temper translates to other programming languages through backends, one per target language. Each backend:

Each backend has an ID, which the -b (--backend) flag of temper build and temper test takes (the command line). By default, temper build builds C#, Java, JavaScript, Lua, Python, and Rust; C++ is built when asked for (-b cpp).

Language ID Targets Package Tests run with
C++ cpp C++14 (C++20 for co_await) a CMake project its own test program
C# csharp .NET 6 (net6.0) a .csproj MSTest
Java java Java 17 a Maven pom.xml JUnit 5
JavaScript js ES modules, with JSDoc types for TypeScript a package.json Mocha
Lua lua Lua 5.1 and later, and LuaJIT a rockspec LuaUnit
Python py Python 3.11 and later a pyproject.toml unittest
Rust rust Rust 1.71.1 and later, edition 2021 a crate cargo test

How each backend is built, and what it chooses for failure, null, naming, and order of evaluation, is in how tempest works: backends and its per-backend table. Getting started with each language is in the getting started guide.

Note: Temper's first compiler also has java8, mypyc, and data backends, and an experimental cppv; tempest doesn't have them yet (inconsistencies).

C++ (cpp)#

Translates Temper to C++14: a header and a source file per module (LIBRARY/init.hpp and init.cpp), a main.cpp that runs the library, and a CMake project. Objects are std::shared_ptrs, and a library's namespace is its name.

Generated code compiles without warnings under -Wall -Wextra -Wpedantic -Werror, and is tested under AddressSanitizer, UBSan, and TSan. C++ leaves the order of evaluation of arguments open, so the backend keeps Temper's left-to-right order wherever it would show. C++20 code can co_await a Temper promise.

Note: Temper's older docs list C++ as "coming soon", and leave it out of this list; tempest's C++ backend passes every functional test (inconsistencies).

C# (csharp)#

Translates Temper to C#. Targets .NET 6.0; .NET Framework 4.8 might be supported in the future.

Translation notes#

Temper function types, such as fn (Int): Int, translate to Func delegates, or Action delegates for a Void result. These have an upper limit of 16 parameters, which isn't accounted for yet. If demand arises, Temper will likely generate new delegate types for more parameters.

Temper nullable types, such as A?, translate to nullable value types or nullable reference types. Nullable reference types need newer versions of C#, so .NET Framework 4.8 support would need to represent these as ordinary reference types.

To show intent clearly, Temper collection types translate to .NET generic interfaces, including "ReadOnly" variants as appropriate. For example, Temper Listed<T> and List<T> both translate to IReadOnlyList<T>, whereas ListBuilder<T> translates to IList<T>. Similar rules apply to Temper's Mapped types and .NET Dictionary types, except that maps created in Temper always keep insertion order. .NET's IList doesn't subtype IReadOnlyList, even though its List implements both, and likewise for Dictionary, so Temper generates overloads of functions taking a Listed that accept any of List, IList, or IReadOnlyList, and likewise for Mapped.

Each Temper library is translated to a .NET project and assembly, and each module to a .NET namespace, with a subdirectory for each subnamespace. Each type defined in Temper gets its own C# file, and a module's top level goes in a static …Global class. Parameter names are part of a C# API, so they're never renamed.

Temper's logging console translates to the Microsoft.Extensions.Logging framework on modern .NET. That framework usually creates loggers by dependency injection, which doesn't suit static classes and static initialization, so Temper generates static methods to give a library its ILogger. Without one, generated libraries log through System.Diagnostics.Trace.

Tooling notes#

Each Temper library gets a .csproj file, so each builds to its own .NET assembly.

The only .NET naming setting in config.temper.md today is csharpRootNamespace. It sets the project's root namespace, the assembly's name, and the presumed NuGet package ID. Microsoft has advice on choosing a namespace.

Temper test blocks translate to the MSTest framework: a test class per module, and a test method per test. Temper provides soft assertions within tests.

Java (java)#

Translates Temper to Java 17 source, and a Maven project (Java 17). A module's top level goes in a …Global class, with a …Main class to run it, and each type gets its own file. Classes also get a Builder, since Java has no named arguments.

Still to write up: how function types translate to single-method interfaces, how nullable types translate, primitive and boxed types, overloads for optional arguments, picking a Java package and Maven identifiers (javaName, javaPackage), and the generated POM.

JavaScript (js)#

Translates Temper to JavaScript ES modules, with types in JSDoc comments for TypeScript, checked with tsc --strict.

Translation notes#

Temper interface types translate to names that work with JavaScript's instanceof operator. Your JavaScript code may use InterfaceType.implementedBy to make classes that implement Temper interfaces.

temper.out/js has a source map for each generated file, so a JavaScript debugger shows the Temper code it came from.

Tooling notes#

Tests translate to Mocha tests, and a package.json makes npm test run them.

Note: Temper's older docs say the JavaScript backend targets ES2018, and its getting started guide says Node 14 or later, while a 0.x release note requires Node 18 (inconsistencies).

Lua (lua)#

Translates Temper to Lua: an init.lua per library, a rockspec, and LuaCATS type annotations, checked with LuaLS. Generated code is declared for Lua 5.1 and later; it's tested on Lua 5.4 and LuaJIT.

Note: a few features need Lua 5.3 or later, such as some string and integer operations; Lua 5.1 itself isn't tested (inconsistencies).

Python (py)#

Translates Temper to Python 3 with type annotations, checked with mypy and pyright in strict mode. Targets Python 3.11 and later; for best support, use CPython.

Translation notes#

Temper's Void type translates to returning Python's None.

Temper default expressions are evaluated as needed, so they don't suffer the singly evaluated default expression pitfall.

Names translate to Python's conventions: snake_case for functions, methods, and properties, and a leading _ for what isn't exported.

Rust (rust)#

Translates Temper to a Rust crate (edition 2021, rust-version 1.71.1). Classes are reference-counted handles (Arc), strings are Arc<String>, failure is a Result, and module state is safe across threads. Generated code passes clippy.