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

From Esolang
Jump to navigation Jump to search

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; or obj[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:

  1. Does obj have a slot named foo? If yes, return it.
  2. 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).
  • this binding: 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.

See Also

Categories