A data-driven programming language developed for the recreational purpose of writing fun software.
Inspired by the book Writing an Interpreter in Go by Thorsten Ball
Me:
Hey, I want to write a Golang interpreter for a language. Define the specs of a brand new language, it must be simple and innovative. Surprise me!
Claude.ai:
Alright! Let’s try something innovative and out of the ordinary. Here's a brand new language concept called "Whirl"—it’s designed to challenge conventional programming paradigms with simplicity and a unique twist on flow and data manipulation.
Sadly Whir already exists and it's far too esoteric for my palate, so Whirlpool was born.
Want to play with whirlpool? Clone the repo and give a go to the terminal REPL available:
$ git clone https://github.com/Hyla96/whirlpool.git
$ go run main.go
Whirl is built around data flow rather than traditional control flow. Programs in Whirl look more like a series of transformations, where data moves through pipes.
No traditional loops or if-else blocks, just data transformations and routing.
The building blocks of Whirl are Spirals and Nodes:
- Spirals: Represent a flow of data, similar to pipelines.
- Nodes: Represent operations that transform data.
Spirals are circular in nature, meaning data can pass through them continuously unless explicitly broken. Each pass modifies the data.
Whirl uses implicit types and relies on the transformations applied to define the shape of data:
- Primitive Types:
num
,txt
,bool
,list
- Transformative Types:
wave
,pulse
,current
Primitive types are ordinary, but the transformative types are dynamic data types that can change with each pass through the Spiral.
Siphons: Think of these as valves that decide whether data flows through. They are akin to conditional logic, but they operate based on the state of the data:
siphon num > 10 -> pipe
Flickers: Temporal states. Data can be stored in flickers, a short-term, volatile memory:
flicker num = 42
Cyclones: These are accumulators or reducers, pulling together data over several passes:
cyclone total -> add
Basic Program Flow: In Whirl, code is a series of transformations. For example, to add 1 to a number, filter out the numbers that are even, and print them:
spiral num -> add 1 -> siphon num % 2 != 0 -> output
In this example,
spiral
represents the flow of numbers,add 1
adds to each number, the siphon filters odd numbers, andoutput
prints them.Fibonacci Sequence (No loops, just flow):
flicker prev = 0 flicker curr = 1 spiral -> flicker next = prev + curr -> cyclone (prev, curr) -> rotate
The Fibonacci sequence is generated by constantly rotating and accumulating two numbers, without a traditional loop.
Data flows continuously through channels. Think of them as conveyors of data across different Spirals. Data can be passed from one part of a program to another:
channel num -> spiral_a -> spiral_b
Channels link Spirals together, making it possible to separate logic into discrete flows.
Glitches: Instead of exceptions or error codes, Whirl uses Glitches, which bubble up through Spirals. Glitches must be handled via special filters:
glitch -> siphon is_glitch -> recover
If an error occurs, it moves through a glitch channel, where it’s either handled or allowed to propagate until recovery.
Whirl doesn’t have functions in the traditional sense. Instead, you define shapes, which are reusable flow patterns:
shape double -> spiral num -> multiply 2 -> output
Shapes can be plugged into different Spirals, effectively working like functions.
- Rather than mutable variables, Whirl works with state transitions. Data enters a state within a Spiral, gets transformed, and moves on. Once data leaves a state, it no longer exists.
Whirl is designed to be both text-based and visual. The text syntax you write can be directly mapped to a visual representation where Nodes are boxes and Spirals are arrows. This makes debugging and program understanding much easier.
Example visual for:
spiral num -> add 1 -> siphon num % 2 != 0 -> output
- A circular node labeled "spiral num"
- Arrows pointing to transformations: "add 1"
- A branch node for "siphon num % 2 != 0"
- An endpoint "output"
Flow Over Control: Whirl eliminates loops, conditionals, and variables in favor of flow-based design. Everything moves continuously through a Spiral.
Temporal Constructs: Features like flickers and cyclones introduce temporal states and reduction in a very organic way.
No Functions, Only Shapes: Instead of the function/method paradigm, reusable logic is encapsulated in shapes, which plug directly into the flow of data.
Visual Hybrid: Whirl can be visualized directly from the code, making it ideal for dataflow-driven debugging.
Prime Numbers:
spiral num -> add 1 -> siphon is_prime(num) -> output
Word Counting in Text:
spiral txt -> split " " -> cyclone count -> output count
HTTP Request (Async by Nature):
channel request -> siphon is_ready -> cyclone response -> output
With Whirl, programming becomes a continuous flow of data, where operations are nodes in a giant feedback loop of transformations. It’s designed to feel different from conventional languages, emphasizing movement over static logic.
What do you think of this? It should definitely be a new challenge when writing the interpreter!