Here is an example of Thun code:
[[[abs]ii <=][[!=][pop !-]or]and][[!-][[++]][[--]]ifte dip][[pop !-][--][++]ifte]ifte
But you wouldn't write it like this. Some layout helps:
[ [[abs] ii <=]
[
[!=] [pop !-] or
] and
]
[[ !-] [[++]] [[--]] ifte dip]
[[pop !-] [--] [++] ifte ]
ifte
But even this is not in the spirit of Thun code. Instead we prefer small definitions, functions specified as names (or symbols) followed by expressions:
p0 [abs] ii <=
p1 [!=] [pop !-] or
p [p0] [p1] and
then [ !-] [[++]] [[--]] ifte dip
else [pop !-] [--] [++] ifte
square_spiral [p] [then] [else] ifte
This might still seem unreadable but I promise with a little familiarity it becomes just as legible as any other notation.
So what does it do?
This function accepts two integers on the stack and increments or decrements one of them such that the new pair of numbers is the next coordinate pair in a square spiral (like the kind used to construct an Ulam Spiral).
It's adapted from the original code on StackOverflow:
If all you're trying to do is generate the first N points in the spiral (without the original problem's constraint of masking to an N x M region), the code becomes very simple:
void spiral(const int N)
{
int x = 0;
int y = 0;
for(int i = 0; i < N; ++i)
{
cout << x << '\\t' << y << '\\n';
if(abs(x) <= abs(y) && (x != y || x >= 0))
x += ((y >= 0) ? 1 : -1);
else
y += ((x >= 0) ? -1 : 1);
}
}
We're going to reimplement that if statement in Thun. We'll make a function that expects two integers on the stack representing x and y and computes the next pair of integers.
p0We need a function that computes abs(x) <= abs(y), we can use ii
to apply abs to two values.
joy? 23 -2 [abs] [ii] trace
23 -2 [abs] • ii
23 • abs -2 abs
23 • -2 abs
23 -2 • abs
23 2 •
23 2
(The trace facility is only available in the Python implementation.)
Then compare them with <=
and that's our first little function:
p0 [abs] ii <=
p1There are two "short-circuiting" Boolean combinators and and or that each accept two quoted predicate programs, run the first, and conditionally run the second only if its result is required to compute the final Boolean value.
We can translate x != y || x >= 0 as:
p1 [!=] [pop !-] or
pWe combine the two -sub-predicates with and:
p [p0] [p1] and
Turning to the branches of the main if statement:
x += ((y >= 0) ? 1 : -1);
Rewrite as a hybrid (pseudo-code) ifte expression:
[y >= 0] [x += 1] [X -= 1] ifte
Change each C phrase to Thun code:
[0 >=] [[++] dip] [[--] dip] ifte
Factor out the dip from each branch:
[0 >=] [[++]] [[--]] ifte dip
Similar logic applies to the other branch:
y += ((x >= 0) ? -1 : 1);
[x >= 0] [y -= 1] [y += 1] ifte
[pop 0 >=] [--] [++] ifte
So here are the two branches of our ifte:
then [ !-] [[++]] [[--]] ifte dip
else [pop !-] [ -- ] [ ++ ] ifte
We can assemble the three functions we just defined in quotes and give
them them to the ifte combinator:
square_spiral [p] [then] [else] ifte
So that's an example of Joy code. It's a straightforward translation of the original. It's easy to see that the function is a branch with two quasi-symmetrical paths.