The Temper programming language¶
The Temper programming language cross-translates to other programming languages.
// Temper
export let half(x: Int): Int {
x / 2
}
%%{ init : { "flowchart" : { "curve" : "stepBefore" }}}%%
flowchart LR
Start[ ]
style Start fill:#FFFFFF00, stroke:#FFFFFF00;
TemperCompiler["Temper Compiler"]
Etc["…"]
JSTS["JS/TS"]
Start --> TemperCompiler
TemperCompiler --> Java
TemperCompiler --> JSTS
TemperCompiler --> Python
TemperCompiler --> Etc
From a single specification, above, you get libraries that can be shared with many programming language communities.
// Temper source from above figure
export let half(x: Int): Int {
x / 2
}
namespace Example.Numerics
{
public static class NumericsGlobal
{
public static int Half(int x__4)
{
return x__4 / 2;
}
}
}
package com.example.numerics;
public final class NumericsGlobal {
private NumericsGlobal() {
}
public static int half(int x__1) {
return x__1 / 2;
}
}
const {
trunc: trunc__1
} = globalThis.Math;
/**
* @param {number} x_0
* @returns {number}
*/
export function half(x_0) {
return trunc__1(x_0 / 2);
};
local temper = require('temper-core');
local half, exports;
half = function(x__1)
return temper.idiv(x__1, 2.0);
end
;
exports = { };
exports.half = half;
do
return exports;
end
def half(x__1: 'int') -> 'int':
return x__1 // 2
Temper is designed, from the ground up, to translate well, so many more coming.
Those libraries can include rich type definitions.
let { Date } = import("std/temporal");
/** Some minimal info about a person. */
export class Person(
public name: String,
public dateOfBirth: Date,
) {
public ageAt(date: Date): Int {
Date.yearsBetween(date, dateOfBirth)
}
/** The count of whole years between their DOB and today in UTC. */
public get age(): Int { ageAt(Date.today()) }
}
using S = System;
using T = TemperLang.Std.Temporal;
namespace Example.Person
{
public class Person
{
readonly string name__14;
readonly S::DateTime dateOfBirth__15;
public int AgeAt(S::DateTime date__17)
{
return T::TemporalSupport.YearsBetween(date__17, this.dateOfBirth__15);
}
public int Age
{
get
{
S::DateTime t___58 = T::TemporalSupport.Today();
return this.AgeAt(t___58);
}
}
public Person(string name__22, S::DateTime dateOfBirth__23)
{
this.name__14 = name__22;
this.dateOfBirth__15 = dateOfBirth__23;
}
public string Name
{
get
{
return this.name__14;
}
}
public S::DateTime DateOfBirth
{
get
{
return this.dateOfBirth__15;
}
}
}
}
package com.example.person;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit.YEARS;
import java.time.ZoneId;
import static java.time.ZoneOffset.UTC;
public final class Person {
public final String name;
public final LocalDate dateOfBirth;
public int ageAt(LocalDate date__11) {
return YEARS.between(date__11, this.dateOfBirth);
}
public int getAge() {
LocalDate t_48 = LocalDate.now(ZoneId.ofOffset("UTC", UTC));
return this.ageAt(t_48);
}
public Person(String name__16, LocalDate dateOfBirth__17) {
this.name = name__16;
this.dateOfBirth = dateOfBirth__17;
}
public String getName() {
return this.name;
}
public LocalDate getDateOfBirth() {
return this.dateOfBirth;
}
}
import {
dateYearsBetween as dateYearsBetween_0,
dateToday as dateToday_1
} from "@temperlang/core";
export class Person {
/** @type {string} */
#name_2;
/** @type {globalThis.Date} */
#dateOfBirth_3;
/**
* @param {globalThis.Date} date_5
* @returns {number}
*/
ageAt(date_5) {
return dateYearsBetween_0(date_5, this.#dateOfBirth_3);
}
/** @returns {number} */
get age() {
let t_7 = dateToday_1();
return this.ageAt(t_7);
}
/**
* @param {string} name_8
* @param {globalThis.Date} dateOfBirth_9
*/
constructor(name_8, dateOfBirth_9) {
this.#name_2 = name_8;
this.#dateOfBirth_3 = dateOfBirth_9;
return;
}
/** @returns {string} */
get name() {
return this.#name_2;
}
/** @returns {globalThis.Date} */
get dateOfBirth() {
return this.#dateOfBirth_3;
}
};
local temper = require('temper-core');
local Person, exports;
Person = temper.type('Person');
Person.methods.ageAt = function(this__0, date__11)
return temper.date_yearsbetween(date__11, this__0.dateOfBirth__9);
end
;
Person.get.age = function(this__1)
local t_0;
t_0 = temper.date_today();
do
return this__1:ageAt(t_0);
end
end
;
Person.constructor = function(this__3, name__16, dateOfBirth__17)
this__3.name__8 = name__16;
this__3.dateOfBirth__9 = dateOfBirth__17;
do
return nil;
end
end
;
Person.get.name = function(this__20)
return this__20.name__8;
end
;
Person.get.dateOfBirth = function(this__24)
return this__24.dateOfBirth__9;
end
;
exports = { };
exports.Person = Person;
do
return exports;
end
from builtins import str as str0, int as int2
from datetime import date as date1
from temper_core import years_between as years_between_53, \
date_today as date_today_54
class Person:
'Some minimal info about a person.'
name__8: 'str0'
dateOfBirth__9: 'date1'
age__2: 'int2'
__slots__ = ('name__8', 'dateOfBirth__9', 'age__2')
def age_at(this__0, date__11: 'date1') -> 'int2':
return years_between_53(date__11, this__0.dateOfBirth__9)
@property
def age(this__1) -> 'int2':
'The count of whole years between their DOB and today in UTC.'
t_48: 'date1' = date_today_54()
return this__1.age_at(t_48)
def constructor__15(this__3, name__16: 'str0',
dateOfBirth__17: 'date1') -> 'None':
this__3.name__8 = name__16
this__3.dateOfBirth__9 = dateOfBirth__17
def __init__(this__3, name__16: 'str0',
dateOfBirth__17: 'date1') -> None:
this__3.constructor__15(name__16, dateOfBirth__17)
@property
def name(this__20) -> 'str0':
return this__20.name__8
@property
def date_of_birth(this__24) -> 'date1':
return this__24.dateOfBirth__9
Sharing type definitions extends type guardrails across network gaps leading to more-reliable code, and simplifies data on the wire.
Temper meets other languages where they are.
Temper functions and types translate to regular functions and types—no awkward native or foreign function interface.
// Temper
/**
* The diff function compares two inputs,
* and computes a [Patch]: the changes
* from [left] required to derive [right].
*/
export let diff<T …>(
left: List<T>,
right: List<T>,
/** Are two items the same? */
eq: fn (T, T): Boolean =
fn (a: T, b: T): Boolean { a == b }
): Patch<T> {…}
$ node
Welcome to Node.js v18.9.1.
Type ".help" for more information.
> let diffLib = await import(
"./temper.out/diff-lib/js/myers-diff.js");
undefined
> diffLib.diff
[Function: diff]
> ^D
$ python3
Python 3.10.6 (main, Aug 30 2022, 05:12:36) [Clang 13.1.6 (clang-1316.0.21.2.5)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from difflib.myers_diff import diff
>>> type(diff)
<class 'function'>
>>> ^D
Temper enables deep supply-chain integration. Translating both production and test code helps ensure the quality of the libray and the translation.
Most engineering organizations use multiple languages: Python for data science, others for mobile apps, JavaScript on the web, and a different language on the server.
Temper helps break down silos by letting a small team maintain core type definitions and logic to all the other silos.
Software Development Toolkits (SDKs)¶
SDKs ease connecting to a service. Client code gets responses as rich, well-typed values.
Letting the service maintainer run a bit of code before the request is sent and right after the response is received, gives great flexibility when deprecating and evolving application programming interfaces (APIs).
Common Infrastructure Teams¶
A team using Temper can make many other teams more productive by solving common problems once regardless of language siloes.
Artificial Intellgence/Machine Learning (AI/ML) Integration¶
Many AI/ML projects fail to provide value due to difficulty integrating them with traditionally engineered systems.
Sharing type definitions and related computations, including presentation logic, helps quickly integrate AI/ML deployments.
Code Migration¶
Migrating old or acquired code is easier if you can extract key definitions and business logic into Temper, and actively maintain it while using it, via translation, in both the old and new systems.
Language Agnostic Solutions¶
Some problems aren't about any programming language. For example, turning untrusted HTML into trustworthy HTML is about HTML, not whether the transformation happens on a server or a client. Written in Temper, one solution can serve many language communities.
Lightweight Standards¶
Lightweight standards documents help engineers and non-engineers collaborate on solving a problem efficiently.
① Requirements in prose explain the why.
② Code in Temper provides an implementation to all engineering stakeholders.
③ Requirements can be checked by in-line test code.
The document stays relevant to engineers because they get libraries they can use. Non-engineering stakeholders have a single engineering point of contact when requirements need to change, so many more business policy changes happen without high-effort change management.