Skip to content
A new, experimental programming language
JavaScript HTML
Branch: master
Clone or download
Latest commit 0264f03 Jun 20, 2018


travis dependencies


In order to try it out, run:
$ npm i
$ node dist/cli <chi-file>

This will tokenize the file <chi-file> and then parse, type-check and interpret it.

Please do not use chi in production yet. It's still highly experimental and will likely change a lot.

About chi

Chi, in its current form, is a meant to be a statically-typed, imperative programming language featuring ideas from the functional programming paradigm. As such, it considers functions to be first-class and performs automatic currying. It uses static scope and supports Unicode tokens. A (potentially incomplete) showcase of features is listed in the next section. Again, note that some features may break or change as chi isn't stable yet.


There is a very simple demo here, where you can play around with chi.

Getting started


Variables allow you to bind a value to a name. Note that there is no significant whitespace and semicolons are required to end a statement.

let a = 3;


Chi supports static typing. If no type is specified, a type will be inferred. Mathematical operations where the operands are of different numeric types will produce a result in the number type with the greater domain. The set of types currently consists of:

  • i8
  • i16
  • i32
  • u8
  • u16
  • u32
  • string
  • bool

There are two special types that are useful for casting. They are resolved to actual types at compile-time:

  • i
  • u

These latter types will cast a value to its equivalent unsigned or signed value. For instance, a:u will cast a such that it is unsigned. If a is of type i8, the cast will end up as u8, et cetera.

Numbers, type casting

There are currently the signed integer types i8, i16, and i32 and their unsigned equivalents u8, u16, and u32 for numerical values. If you don't specify the type of an integer literal, it will be assumed to be of type i32. In the example below, the first line will be 255:i8 + 2:i16, which is equivalent to -1:i8 + 2:i16, which is equivalent to -1:i16 + 2:i16, which will be evaluated to 1:i16.

The second line demonstrates that 1 + 128:i8 is equivalent to 1:i32 + 128:i8, and will by the same rules be evaluated to -127:i32.

255:i8 + 2:i16;
1 + 128:i8;

You can also perform cast chaining as demonstrated below. This will result in 2258274, a 32-bit integer, being cast to a 16-bit integer which will then be casted to an 8-bit integer. (This is equivalent to casting the 32-bit integer to an 8-bit integer directly, but there are indeed use cases where casting a value multiple times can turn out to be beneficial.)


You can also cast entire expressions:

let a = 1024;
(a:i8 + a):i16;


A string literal can be constructed using double quotes. The operator + is overloaded to handle string concatenation if both operands are strings. Mixing types will throw an error. " can be escaped within a string using \".

let p = "hello";
let q = " world";
p + q;

You can further cast strings to any fixed-width integer type. For instance, the following statement evaluates to 6:i32:

"2":i * "3":i;

Function expressions

Function expressions allow you to abstract your code over variables. Note that in chi, you don't need an explicit return statement, as blocks evaluate to their last expression. So, in the example, sum1 and sum2 are semantically equivalent.

let identity = x => x;
let identity2 = (x) => x;
let sum1 = (a, b) => a + b;
let sum2 = (a, b) => {
	let sum = a + b;

Automatic currying

In chi, you don't need to explicitly return a lambda function that closes over a certain environment. Instead, if whatever a function returns doesn't have enough parameters bound in the environment, it becomes a closure. This means, curry1 (which either returns a closure for one argument, or a value for two arguments) and curry2 (which behaves exactly the same) can be used interchangeably.

let curry1 = x => y => x + y;
let curry2 = (x, y) => x + y;
curry1(5, 8);
curry2(5, 8);

Arity checking

If you try to invoke a function with more arguments than it can receive, this can be detected due to static type checking and throw a TypeError.

let f = (x, y) => x * y - 3;
f(1, 2, 3);

Unicode tokens

Chi also allows certain tokens to have alternative forms. For example, the MINUS token can either be Hyphen-minus, En Dash or Em Dash (-, and respectively). Further examples how this can benefit esthetics can be seen below.

let y = (x, y) => 2¹⁰ · x² – y⁴;
let formula = truefalsetrue;
You can’t perform that action at this time.