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)