I’m hitting on something deeply twisted this week. It’s called homespring. Homespring is interesting for two reasons. First, it’s got a sort of reverse flavor to it: it consists of active agents in a passive structure. So, for example, you can’t do anything like control flow – that’s a dynamic change in the control flow of the program; in Homespring, you need to trick the agents into doing something using the static, unchanging structure of the program. And second, it challenges you to write your program in the form of a poem! And the icing on the cake? The agents are salmon,
and the passive structure is a network of streams.
The syntax of Homespring is set up to look as much like english as possible. But don’t let that
fool you – the english syntax is just an artifact, a facade. It doesn’t parse anything like english.
The tokens consist of groups of characters things separated by single spaces or carriage returns, with “.” as an escape character. So for example, “Hello world” is two tokens; “Hello. World” is one token – the “.” is an escape character that turns the space between “Hello” and “World” into a part of a single token consisting of the string “Hello World”. To make matters a bit more confusing, since unescaped spaces always separate tokens, if you’ve got two spaces next to each o, then they separate an empty token between them. “Hello World” is actually three tokens, “Hello”, “”, and “World”.
The way that the program is parsed is quite straightforward. The program is parsed into a simple
tree of tokens; each token is a child of the token immediately before it, with one exception. If there
is an empty token, then the parser jumps back up one level in the tree, and adds the next token to the previous node up the tree. This diagram shows you a few examples.
The root of the tree is the ocean; each node in the tree is either a spring which is a source of water and also where salmon can spawn (if their name is not a keyword), or a primitive object (if they have a keyword name); paths in the tree between springs and the ocean are streams. Salmon
are created by reading lines of input: each line of input is wrapped in a salmon, which tries to swim upstream until it reaches its home spring – that is, a spring whose name matches the string it wraps. If it reaches either its home or a leaf of the tree, it will spawn. When a salmon spawns,
it becomes mature, creates a new salmon which contains the name of the spring, and both the salmon and its spawn start to swim downstream to the ocean. When a salmon swims downstream and reaches the ocean, it’s string is output.
I’ll explain the primitives as we go along. Let’s start with an example. The easiest example to write is almost the good old “Hello world”, except that this version is basically an endless loop.
bear hatchery Hello,. World.. powers
First, let’s see how that parses. “bear” is followed by a single space, so it’s a token, and it’s at the ocean. “hatchery” is also followed by a single space so it’s a token, and it’s places as a child of “bear”. Then we’ve got “Hello,. World”. The space is quoted by a “.”, so that’s one token, and it’s places as a child of “hatchery”. There there are two spaces – so there’s an empty token, so the insertion point for new tokens moves up one level – inserts will happen as children of “hatchery”. And finally, there’s “powers”, which will be placed as a child of “hatchery”. So the parse tree is:
To understand what this does, we need to understand the three primitives in here: “bear”, “hatchery”, and “powers”.
- The “bear” primitive eats mature salmon, but won’t eat salmon that haven’t spawned. So
it will prevent any mature salmon from getting to the ocean. - The “hatchery” primitive spawns young salmon containing the string “homeless” which swim upstream. It only spawns while it is receiving electricity from downstream.
- The “powers” primitive is a hydro plant which generates electricity, and sends it upstream.
So what happens in this program is that the “powers” generates electricity which feeds the “hatchery”. The hatchery spawns homeless young salmon, which swim upstream to “Hello, World”, and then,
since they’re at a leaf, they spawn, turning mature, and swim downstream. The young salmon that they spawn contain the string “Hello world.”, and they also swim downstream. The hatchery doesn’t affect the salmon swimming past it, so they get to the bear. The bear eats the mature salmon, leaving only the young, which get to the ocean – and so “Hello, World.” gets output for each salmon that reaches the ocean. Since the hatchery is being powered continuously, it keeps spawning salmon, so there will be a continuous flow of salmon getting to the ocean and printing.
So, what about doing it just once? Here’s where it gets really silly. Here’s a halting program
that prints “Hello World!” once:
Universe of bear hatchery says Hello. World!. It powers the marshy things the power of the snowmelt overrides.
Before we look at the parsetree for that, let’s go through the new primitives.
- “snowmelt”: a source of water that flows downstream, one node each step. Many things
are destroyed by snowmelt. - “marshy”: a marshy node is a node that slows snowmelt. Snowmelt takes two steps to
pass through a marshy node. - “universe”: the world of the program. If snowmelt hits the universe, the program halts.
So, let’s see the parsetree, and work through how the program works. When the program
starts (time=0), two things happen. Power from the power plant starts to flow, and snowmelt starts to run upstream. At time=1, the snowmelt is at “the”; and the power reachers “It”. At time=2, the snowmelt reaches “of”, and the power reaches the hatchery, which turns on. At time=3, the hatchery produces a salmon which swims up to “says”, and the snowmelt reaches “power” (not “powers”, which would be another power plant. This is just a spring.) At time=4, the salmon reaches “Hello world”, turns mature, and spawns, and the snowmelt reaches “the”. At time=5, both the newly spawned young “Hello world” salmon and the mature homeless salmon reach “says”; and the snowmelt reaches “things”. At time=6, the two salmon get back to the hatchery, and the snowmelt reaches the marsh. At time=7, the two salmon reach “bear”, and the mature salmon gets eaten. The snowmelt is doesn’t move; it’s slowed by the marsh. At time=8, the young salmon reaches “of”, and the snowmelt gets out of the marsh and reaches “the”. Finally, at time=9, the young salmon reaches “Universe” which is the root of the tree, and thus the ocean, so it gets to print. Then the snowmelt arrives, hits Universe, and the program halts. Simple, right?.
There are a ton of other primitives that I’m not going to go through; I strongly recommend that you visit the Homespring site, and read the language reference yourself. It’s a great piece of work. And this is a really fun language to play with; one of the goals of the language is for your
programs to read as poems – the challenge is to figure out how to write it as a poem with a natural flow, and yet still have all of the timings work out right! The manual also includes several other wonderful sample programs.
Just to give you a flavor, here’s a longer program – it’s a program that inputs two single-digit numbers, and prints out their sum.
Universe is marshy but evaporates downstream. Sense the rapids reverse. Down bridge is now marsh: Marshy marshy marshy marshy marshy marshy marshy marshy marshy marshy now. All evaporates downstream. Sense the rapids now: Rapids rapids rapids rapids rapids rapids rapids rapids sensed. Ugh +. Take powers from snowmelt therefore; the current time is of youth. Fountain is young. Bear cannot reverse. Down inverse. Lock young. Switch young. Range. Switch clone to the switch itself. Now inverse. Lock narrows down: Powers to append. Up go all young. Bear time evaporates then. Therefore: Spawn power. Invert evaporates it. Down force. Down reverse. Down net. The net reverses force. Now try: Add add add add add add add now. It is not possible; now count: 0. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18+. You can now pump in reverse. Down lock goes; narrows lock down: Inverse. Lock young. Range. Sense 0n 1n 2n 3n 4n 5n 6n 7n 8n 9n Powers lock time now. Inverse. Lock young. Range. Sense 0n 1n 2n 3n 4n 5n 6n 7n 8n 9n Powers snowmelt now. Powers all: Bear hatchery n powers insulated bear hatchery ?. Hydro. Power spring as snowmelt powers snowmelt then, and disengage. HYDRO!!
Incredible language! Cant wait to try it out!
Theres a typo, hatchery creates “homeless” not “nameless”
Andrew:
Thanks for the catch! I can’t really call it a typo, because I did it multiple times; for some reason, I just kept reading the language spec as saying “nameless” when in fact it said “homeless”. I think I need more sleep 🙂
It is quite an amazing monstrosity, isn’t it? It’s fascinating. And every time I look at it, I have this nagging sense that there’s something about its idea of active agent/passive structure that isn’t entirely ridiculous, but I can’t quite put a finger on what that might be. Just this nagging sense that there’s something there.
Ah, I remember Homespring – i love how poetic each program is. I always wanted to try it but there wasn’t an interpreter when I first found it – this was ages ago though.
Reading the language specs, the first thing that jumped my mind was that it might be entertaining to have a graphical “IDE” of sorts, with animated springs and salmons and stuff. It would be a bit like the Incredible Machines. If I only had the time to write that…
It is quite an amazing monstrosity, isn’t it? It’s fascinating. And every time I look at it, I have this nagging sense that there’s something about its idea of active agent/passive structure that isn’t entirely ridiculous, but I can’t quite put a finger on what that might be. Just this nagging sense that there’s something there.
Well, that’s basically what an electrical circuit is, no? To some extent. Gates and transistors define a passive structure, electrons actively move through it seeking ground or whatever…
Reading the language specs, the first thing that jumped my mind was that it might be entertaining to have a graphical “IDE” of sorts, with animated springs and salmons and stuff. It would be a bit like the Incredible Machines. If I only had the time to write that…
I a little bit wonder if you could execute Homespring programs by converting the program into a starting state in Life and then just letting it run. It seems, as long as bears/fish/other actors can’t jump from place to place or share single tree nodes with an unbounded number of other actors, like that ought to be possible even if intractable to engineer. The whole thing seems a lot like cellular automata moving around within a tree to me.
as long as bears/fish/other actors can’t jump from place to place
Er, what I meant to say here was “jump from place to remote place instantaneously”.
Coin:
That’s it! You’ve finally made me remember just what about Homespring has been itching away inside my brain.
In grad school, while I was looking for a PhD project, I worked for a while on a project that was exploring the idea of simulating physical processes by creating a grid of processors describing the space, and having programs representing objects moving around the grid – the program would move itself the way that the object it represented would. It was almost like a CA, except that the computations that could occur in a grid cell were quite complicated.
But that’s what was lurking in the back of my mind – that fixed spatial grid, with the programs moving around.
Coin, the language doesn’t resemble electrical circuits at all. Electrons tend to be quite a bit more mobile that the state transitions of logic gates, which of course don’t exhibit “crisp” states at all, but quantities, such as voltages, vary over a (virtually) continuous range of values over time.
There are some similarities to synchronous logic, however, where larger blocks, consisting of several logical gates, separated by latches, run in lock step directed by a global clock signal. But even then, Homespring doesn’t allow cyclic graphs, so there’s no obvious mapping between the two.
Mark: Interesting, what was the point of giving each grid cell its own processor instead of just doing it in a single processor and simulating the grid?
Flaky, yeah, good points.
Coin:
That’s exactly why I left that project. There was no point in building a custom machine for it – you could take almost any standard off-the-shelf parallel machine and write a simulator to run the programs; but the PI was deeply committed to the idea that this was a new hardware design, not a new programming model. But the hardware design was a loser – the kind of synchronization that he designed into it would slow it down so much for any reasonable grid size that you’d lose the benefits of parallelism.
I still think it would be incredibly cool to build a programming language based on the concept of physical-objects-as-code moving around in space-as-processing-grid. We never got very far with working out how to program it, because the PI was more interested in figuring out how to build the machine, but the bits we did were really fascinating. For example, for orbital physics, you build matter particles – and the matter particles both “radiate” gravitons (meaning launches little programs in neighboring cells that will spread out, and keep track of how far they’ve gone); and also react to impacts from gravitons (by accelerating in the appropriate way based on how many gravitons are hitting them, where they came from, and how far they’ve travelled).
The inverse problem involves poetry about programming languages. Example:
On the Programming Language SIMSCRIPT II.5
a software poem by Jonathan Vos Post
(With Apologies to Samuel Butler’s
Hudibras “A Babylonish Dialect…”)