We are currently working on new rules for what content should and shouldn't be allowed on this website, and are looking for feedback! See Esolang:2026 topicality proposal to view and give feedback on the current draft.
The Wind Chaser
The Wind Chaser is designed by PSTF and his AI assistant.
This programming language was originally called 'Reflexio', but it was later renamed 'The Wind Chaser' by PSTF. It's worth mentioning that 'The Wind Chaser' is also the name of a character created by PSTF.
Overview
The Wind Chaser is a dynamic, pure prototype-based programming language. Inspired by Self, Io, and JavaScript's prototypal model, it strips away classes in favor of direct object delegation. Everything is an object, and new objects are created by cloning existing ones. It is Turing-complete, supports arbitrary-precision decimal arithmetic, and offers first-class functions with lexical closures (enclosures).
Syntax
Core Principles
- Everything is an Object – Numbers, strings, lists, functions, and even the base prototypes are objects.
- Prototypes & Delegation – Objects inherit behavior by delegating to a parent object (the prototype). If a slot (property/method) is not found on an object, the runtime follows the _proto chain.
- Cloning over Instantiation – You never write new; you write clone(). The Object prototype provides the base clone method.
- Lexical Closures – Functions capture the environment in which they are defined, enabling proper enclosures.
Lexical Structure & Syntax
- Comment
- Things that start with a hashtag (inline) or are enclosed in
[$ ... $]will be automatically ignored. - Number literal
- A string made up of any characters from 0123456789 is called a number. A decimal point is allowed in the middle, but only one can appear. The precision is unlimited (you can refer to the Arbitrary-Precision Floating Point Numbers I just wrote for how to implement it).
- String literal
- Any Unicode non-surrogate character enclosed in single or double quotes. You can add an f prefix to a string to format it, or an r to make it a raw string. You can also change the quotes to triple quotes to create a multi-line string.
- List Literal
- Any elements included in square brackets, separated by commas.
- Object
- A collection of key-value pairs enclosed in curly braces is called an object. The key has to be a valid identifier, while the value can be any other object. This language follows the principle that everything is an object.
- Function
func (params) { ... }- Variable binding
let name <- expr;- Slot access
- Use dot notation or subscript.
- Slot asignment
obj.name <- expr;orobj[name] <- expr;- Return
return expr;(last expression is implicitly returned)- Conditional branch
if (cond) { ... } elseif (cond) {...} else { ... }- Conditional loop
whilst (cond) { ... }- Iterative loop
for (i in something) { ... }- Arithmetics
- All arithmetic operators (+, -, *, /, %, ^, //) work with arbitrary-precision decimals. A / operation yields a result with up to 50 decimal digits of precision by default (configurable).
Prototypes & Delegation Model
Every object has a hidden slot called _proto. When you read a slot (e.g., obj.foo), the runtime checks:
- Does obj have a slot named foo? If yes, return it.
- If not, recursively check obj._proto.
When you write to a slot (e.g., obj.foo = 42), the slot is always created or updated on the object itself (shadowing any parent slot). This keeps state local and delegation pure.
The Root Prototype: Object
The global Object is the root prototype. Every object inherits from it directly or indirectly.
Methods:
- clone() → returns a new object with _proto set to the receiver.
- hasSlot(name) → returns true if the object has its own slot.
- slots() → returns a list of the object's own slot names.
Data Types & Built-in Prototypes
1. Number (Arbitrary Precision)
All numeric literals are Number objects.
- Operations: +, -, *, /, %, * (power).
- Comparisons: <, >, <=, >=, =, !=.
- Methods: .abs(), .round(places), .to_string().
Global setting: Number.set_precision(100) sets the number of digits for division results.
2. String
Methods: .length, .concat(other), .substring(start, end), .split(delim), .to_upper(), .to_lower(), indexing via [] (e.g., "abc"[1] → "b").
3. List
Dynamic arrays.
- Methods: .push(item), .pop(), .length, .map(fn), .filter(fn), .reduce(fn, init), .join(sep).
- Indexing via [] (e.g., list[0]).
4. Function (Lambdas & Enclosures)
- Functions are ordinary objects. They have a .call(...) method, but syntactically you just do fn(args).
- Lexical scoping: Variables from the outer scope are captured by reference (mutations are visible).
thisbinding: Inside a function called as a method (e.g., obj.method()), this is bound to the receiver (obj). For standalone calls, this is the global object.
I/O Subsystem
printLn(...)- Writes a string representation of each argument to stdout, separated by spaces, ending with a newline.
print(..., sep <- " ", end <- "\n")- Writes but with custom seperation and ending.
readLn(prompt)- Reads a line from stdin and returns it as a String. Returns nil at EOF.
readParse(type, prompt)- Reads a line from stdin and returns its value after it got turned into that type.
Control Flow & Turing Completeness
The Wind Chaser provides:
- Conditional branching: if / else.
- Iteration: whilst/for loops.
- Recursion: Functions can call themselves (and each other) directly.
- Arithmetic & comparisons: Sufficient to construct counters, booleans, and primitive recursion.
Because it supports lambda calculus semantics (anonymous functions + closures + application) and standard primitive recursion (via while or recursion), The Wind Chaser is fully Turing-complete.
Standard Library Snippets
The Global Environment (global)
All top-level let bindings are stored as slots on the global object. You can inspect it via global.slots().
Syntactical Examples
Usage of Closure
let makeCounter <- func (start) {
let count <- start;
return fn (increment) {
count <- count + increment;
return count;
};
};
let counter <- makeCounter(10);
print(counter(2)); # 12
print(counter(5)); # 17
# The closure holds onto `count` and `increment` variable scope.
Mapping List Elements
import math.everything let const phi <- (1 + sqrt(5)) / 2; let const psi <- (1 - sqrt(5)) / 2; let myList <- [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; let fibonacci <- map(myList, func x -> (1 / sqrt(5)) * (phi ^ x - psi ^ x)); print(myList); # [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 134]
I/O Interaction
print("What is your name?");
let name <- read_line();
print("Hello, " + name + "!");
print("Enter a number:");
let num <- parseRead(Number);
let squared <- num ^ 2;
print("Square: " + squared.to_string());
Closures and Mutability
let makePair <- fn () {
let shared <- 0;
let inc <- fn () { shared <- shared + 1; return shared; };
let dec <- fn () { shared <- shared - 1; return shared; };
return { inc: inc, dec: dec };
};
let pair <- makePair();
print(pair.inc()); # 1
print(pair.inc()); # 2
print(pair.dec()); # 1
Full Examples
Babylonian Square Root
# Set high precision for divisions inside the loop
Decimal.set_precision(60);
let sqrt <- fn (n) {
# Initial guess
let guess <- n / 2;
# Tolerance: 1e-50
let tolerance <- 1 / (10 ** 50);
let is_good_enough <- fn (g) {
let diff <- (g * g) - n;
if (diff < 0) { diff <- -diff; } # absolute value
return diff < tolerance;
};
let iter <- fn (g) {
if (is_good_enough(g)) {
return g;
}
# Newton-Raphson: g <- (g + n/g) / 2
let better <- (g + (n / g)) / 2;
return iter(better);
};
return iter(guess);
};
let result <- sqrt(2);
print("Square root of 2:");
print(result);
# Output: 1.414213562373095048801688724209698078569671875376948073176...
print("Check: " + (result * result).to_string());
Guessing Numbers
print("=== Guess the Decimal Number (-50 to 50) ===");
import random.everything;
let secret <- randInt(-50, 50); # The hidden number
let guessed <- false;
let attempts <- 0;
while (not guessed) {
print("Enter your guess:");
let input <- read_line();
# Convert to decimal safely
let guess <- input.to_number(); # Let's assume the String prototype has .to_number()
# (If the language lacks .to_decimal(), we'd use a parser, but we'll define it conceptually)
attempts <- attempts + 1;
if (guess = secret) {
print("Correct! You got it in " + attempts + " attempts.");
guessed <- true;
} else {
let diff <- guess - secret;
if (diff < 0) { diff <- -diff; } # absolute
if (guess > secret) {
print(guess + " is too big! Please try again.");
} else {
print(guess + " is too small! Please try again.");
}
# Give a hint every 3 attempts
if (attempts % 3 = 0) {
if (secret % 2 = 0) {
print("Hint: The number is even.");
} else {
print("Hint: The number is odd.");
}
}
}
}
print("Thanks for playing!");
Implementation Notes (Conceptual)
- Memory Model
- Objects are mutable dictionaries (hash maps) with a pointer to their prototype.
- Decimal Precision
- Under the hood, a C++/Rust/Java big-decimal library handles the arbitrary precision. All numeric literals are parsed into this type.
- Evaluation Strategy
- Strict (applicative-order) evaluation. Arguments are evaluated before function application.
- Garbage Collection
- Automatic reference counting or tracing GC manages the prototype graph.
Epilogue
The Wind Chaser gives you the flexibility of prototypes, the safety of arbitrary-precision math, and the expressive power of functional programming—all in one cohesive, minimalist package.