Whenever is a little known esoteric language where the program is a todo list of items.
Any actionable item can be executed, in whichever order the program feels like.
Oh and there are no variable, no functions, and none of the usual stuff, so it will be fun!
Hello, World!
Hello, World! is very easy - just one todo item to print "Hello, World!"
. As there's no other items on program's todo list, that's what's going to happen.
1 print("Hello, World!");
It does exactly what we'd expect:
$ whenever hello.txt
Hello, World!
Any Order
Let's
1 print("Cats");
2 print("Are Better Than");
3 print("Dogs");
There's 6 possible outputs, here's two of them:
$ whenever anyorder.txt
Dogs
Are Better Than
Cats
$ whenever anyorder.txt
Cats
Are Better Than
Dogs
Loop
Here's a simple loop that prints 1 to 10, without variables.
1 2#9;
2 defer (1) print(11 - N(2));
What the hell is going on here?
-
2#9
means put todo task2
on the list9
more times. -
defer (1)
means that the task2
cannot be done if task1
is still pending -
N(2)
checks how many times task2
is on todo list, including current one. The first time it's executedN(2)
is10
. - So
11 - N(2)
starts as11 - 10
, that is1
- next time line
2
is executed,N(2)
is9
, so11 - N(2)
, that is2
, is printed - and so on until
11 - 1
, that is10
, is printed, and there's nothing more to do
Odd Even
Before we do the Fizz Buzz, let's try something simpler - Odd Even loop.
It's very easy to modify the original program to print "1 is odd"
, "3 is odd"
and so on until some predefined value. We need to adjust the formula to be A - N(lineno) * 2
to step by 2, for some value of A
(we could either calculate with math, or adjust until we find the right value).
It's just as easy to modify the original program to print "2 is even"
, "4 is even"
and so on.
The only difficulty is to make the loops run in sync, and for that they should look at each other's counters in defer
condition:
1 2#4,3#4;
2 defer (1 || N(2) < N(3)) print((11 - N(2) * 2) + " is odd");
3 defer (1 || N(2) == N(3)) print((12 - N(3) * 2) + " is even");
Which generates:
$ whenever oddeven.txt
1 is odd
2 is even
3 is odd
4 is even
5 is odd
6 is even
7 is odd
8 is even
9 is odd
10 is even
Fizz
To keep complexity low, let's do the Fizz - the FizzBuzz version with just the Fizz rule.
We could just do 3 interlocking loops. To keep things simple let's keep them at same number of iterations, so it prints numbers 1 to 18:
1 2#5,3#5,4#5;
2 defer (1 || N(2) < N(4)) print(19 - N(2) * 3);
3 defer (1 || N(3) == N(2)) print(20 - N(3) * 3);
4 defer (1 || N(4) == N(3)) print("Fizz");
Which outputs:
$ whenever fizz.txt
1
2
Fizz
4
5
Fizz
7
8
Fizz
10
11
Fizz
13
14
Fizz
16
17
Fizz
We could do 15 of such loops for FizzBuzz, and add some +1
s to condition to make it print unequal number of them, but how about we try something else?
Emulating Variables
This code is much longer, but it might be a lot easier to understand:
1 2#-1,3#-1,4#-1,6#-1,7#-1;
2 2;
3 defer(1) print(N(2));
4 defer(1) print("Fizz");
5 defer(1 || 3 || 4) 2,3,6;
6 defer(1 || 3 || 4) 2,3,7;
7 defer(1 || 3 || 4) 2,4,5;
It produces infinite Fizz:
whenever fizz2.txt | head -n 20
1
2
Fizz
4
5
Fizz
7
8
Fizz
10
11
Fizz
13
14
Fizz
16
17
Fizz
19
20
There's much less magic here, and it's very systematic way to do programming:
- line 1 initializes all counters. They start as
1,2,3,4,5,6,7
, but we only want to have5
as todo list, so we remove everything else. - to make sure initializer runs, everything except
1
and2
hasdefer(1)
condition. -
2 2;
is a todo list that just reschedules itself when executed, so we basically created a variable. As it does nothing there's no need to putdefer(1)
condition on it. - Whenever doesn't allow mixing
print
statements and line statements, so todo items3
and4
do various kinds of printing. If any printing is todo, everything after that is deferred. - and everything that follows is loop condition.
5
doing2,3,6
is likeN(2)+=1
(2),print(N(2))
(3), andgoto 6
(6).
FizzBuzz
So let's try to do FizzBuzz:
1 2#-1,3#-1,4#-1,5#-1,6#-1,8#-1,9#-1,10#-1,11#-1,12#-1,13#-1,14#-1,15#-1,16#-1,17#-1,18#-1,19#-1,20#-1,21#-1;
2 2;
3 defer(1 || N(2) > 100) print(N(2));
4 defer(1 || N(2) > 100) print("Fizz");
5 defer(1 || N(2) > 100) print("Buzz");
6 defer(1 || N(2) > 100) print("FizzBuzz");
7 defer(1 || 3 || 4 || 5 || 6 || N(2) > 100) 2,3,8;
8 defer(1 || 3 || 4 || 5 || 6 || N(2) > 100) 2,3,9;
9 defer(1 || 3 || 4 || 5 || 6 || N(2) > 100) 2,4,10;
10 defer(1 || 3 || 4 || 5 || 6 || N(2) > 100) 2,3,11;
11 defer(1 || 3 || 4 || 5 || 6 || N(2) > 100) 2,5,12;
12 defer(1 || 3 || 4 || 5 || 6 || N(2) > 100) 2,4,13;
13 defer(1 || 3 || 4 || 5 || 6 || N(2) > 100) 2,3,14;
14 defer(1 || 3 || 4 || 5 || 6 || N(2) > 100) 2,3,15;
15 defer(1 || 3 || 4 || 5 || 6 || N(2) > 100) 2,4,16;
16 defer(1 || 3 || 4 || 5 || 6 || N(2) > 100) 2,5,17;
17 defer(1 || 3 || 4 || 5 || 6 || N(2) > 100) 2,3,18;
18 defer(1 || 3 || 4 || 5 || 6 || N(2) > 100) 2,4,19;
19 defer(1 || 3 || 4 || 5 || 6 || N(2) > 100) 2,3,20;
20 defer(1 || 3 || 4 || 5 || 6 || N(2) > 100) 2,3,21;
21 defer(1 || 3 || 4 || 5 || 6 || N(2) > 100) 2,6,7;
22 defer(N(2) <= 100) 2#-N(2),3#-N(3),4#-N(4),5#-N(5),6#-N(6),7#-N(7),8#-N(8),9#-N(9),10#-N(10),11#-N(11),12#-N(12),13#-N(13),14#-N(14),15#-N(15),16#-N(16),17#-N(17),18#-N(18),19#-N(19),20#-N(20),21#-N(21);
It works perfectly:
$ whenever fizzbuzz.txt
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
16
17
Fizz
19
Buzz
Fizz
22
23
Fizz
Buzz
26
Fizz
28
29
FizzBuzz
...
It's a lot longer than an usual FizzBuzz, but really not that much more complicated:
- initializer in
1
only leaves7
,22
, resets everything else -
2
is the only variable -
3
to6
are variousprint
statements - printing does not progress if initializer is todo, or if
N(2) > 100
-
7
to21
are various loop parts - each increments the variable, schedules some printing and goes to next step of the loop - printing does not progress if initializer is todo, or any printing is todo, or if
N(2) > 100
- finally
22
is exit condition - ifN(2) > 100
it's the only thing that can execute and it sets all other counts to0
.
Another Fizz
Let's try a different approach, starting with just Fizz part again.
1 2#-1,3#-1,4#-1,5#-1,6#-1;
2 2;
3 3;
4 defer(1 || (N(5) + N(6)) != 1) 5#-N(5),6#-N(6);
5 defer(1 || N(3) == 0) print(N(2));
6 defer(1 || 3) print("Fizz");
7 defer(1 || 4) 2,3,-3#((N(3)/3)*3),4,5,6,7;
What's going on here?
- initializer in
1
only leaves7
, resets everything else -
2
is variableN
-
3
is variableN % 3
-
4
-6
are theprint
system. To issue print command all of them should be scheduled exactly once. -
4
sees that one of the print commands finished (so only 1 is remaining), so it clears all of them. -
5
and6
are variousprint
statements, depending onN(3)
, that isN % 3
- condition
3
is shortcut forN(3) != 0
, documentation says!3
should work for the opposite condition, but isn't actually implemented - If we could do print and change in the same statement, we could get away with just
6 defer(1 || 3) print("Fizz"),-5;
and not need4
, but that's just part of Whenever design. -
7
is our loop iteration -
7
increments2
(N
) -
7
increments3
, then it decrements it by((N(3)/3)*3)
. That expression is0
ifN(3)
is less than3
, then it becomes3
. So as a resultN(3)
loops 0, 1, 2, 0, 1, 2, etc. -
7
schedules printing with4
,5
,6
- print system will run either (5
then4
) or (6
then4
), depending onN(3)
-
7
schedules itself to run again, but it will wait for the print system.
Another FizzBuzz
1 2#-1,3#-1,4#-1,5#-1,6#-1,7#-1,8#-1,9#-1;
2 2;
3 3;
4 4;
5 defer(1 || (N(6) + N(7) + N(8) + N(9)) != 3) 6#-N(6),7#-N(7),8#-N(8),9#-N(9);
6 defer(1 || N(3) == 0 || N(4) == 0) print(N(2));
7 defer(1 || 3 || N(4) == 0) print("Fizz");
8 defer(1 || N(3) == 0 || 4) print("Buzz");
9 defer(1 || 3 || 4) print("FizzBuzz");
10 defer(1 || 5 || N(2) >= 100) 2,3,-3#((N(3)/3)*3),4,-4#((N(4)/5)*5),5,6,7,8,9,10;
11 defer(1 || 5 || N(2) < 100) -2#N(2),-3#N(3),-4#N(4),-10#N(10);
It follows all patterns from the previous Fizz - with just extra variable for N%5
(4
), a few more prints (5
-9
), loop exit condition (11
), and extra check in main loop iteration for loop exit (10
).
Should you try Whenever?
I stumbled upon this language entirely by accident, I've never seen anything like it, and as far as I can tell it's virtually unknown even among esoteric language lovers.
The existing Java interpreter is a bit low quality:
- it doesn't compile on latest Java without some trivial tweaks (
enum
is now a keyword so quite search and replace) - it doesn't have good debug output (what we want is state)
- it doesn't optimize away NOPs, so it can be extremely slow, trying to execute do-nothing code (variables like
2 2;
or code with defer checks preventing it from running). - it doesn't even fully follow its own spec, especially lacking
!
operator
But language itself is just brilliant. I hope someone writes a better interpreter for it, with in-browser visualization. To be honest I like it so much I might even do it at some point.
I definitely recommend giving it a try.
Code
All code examples for the series will be in this repository.
Top comments (0)