A better 404 polyglot
Between 2009-ish and 2018, StackOverflow’s 404 page displayed the following polyglot program:
# define v putchar
# define print(x) ⏎
main(){v(4+v(v(52)-4));return 0;}/*
#>+++++++4+[>++++++<-]>⏎
++++.----.++++.*/
print(202*2);exit();
#define/*>.@*/exit()
This program was originally due to Mark Rushakoff, who later proposed a shorter version:
# define v putchar
# define print(x) main(){v(4+v(v(52)-4));return 0;}/*
#++++++++4[>++++++<-]>++++.----.++++<*/
print(202*2)
#undef /*>.@*/v
The claim was that this polyglot worked in “Befunge-93, Brainf*ck, Python, Ruby, Perl, and C.”
(And it’s also valid PHP.)
But it never really worked in Befunge-93, because all it does is 4*.@. Apparently
Mark had used some interpreter with a sort of implicit string-mode, where e was a command
meaning “push the ASCII value of e.” In actual Befunge-93 e is a no-op, and in Funge-98
e means “push hex e, i.e., 14,” so that part of the polyglot worked only in that one
non-standard interpreter.
This morning I nerdsniped myself into trying to write a more aesthetically satisfying polyglot 404. My finished product is as follows (tio.run):
#define puts(v)int main(){puts(/*+>+>+
puts=print#[>+[-<+++>]<<]>.@*/"404");}
puts(("----.++++.>++++++++++"and 404))
#undef puts/*>"oops-"-+++^<notfound.*/
This polyglot works in Befunge-93, Brainfuck, C89, Python, and Ruby. (No more Perl or PHP.)
In Brainfuck, C89, Python, and Ruby, my polyglot prints exactly "404\n" to
standard output. In Befunge-93, it prints "404 " instead — which is
unfortunate, but basically I decided that it was more interesting
to reuse the . character (“print ASCII” in Brainfuck,
“print integer” in Befunge) than to create two completely
different paths for Brainfuck and Befunge.
The Python print function prints a newline after whatever you give it.
The Ruby print function does not print a newline, but
a different Ruby function, puts, does. (And C provides only puts.)
I was surprised to learn that in both Python 3 and Ruby, the following code is legal:
puts=print
puts(404)
In Python 3, I know what it’s doing: print is a built-in function,
and so we’re creating a variable puts in the current (global) scope
with initial value print. (Functions are more or less first-class
objects in Python.) So then when we call puts(404), we’re really
calling print with 404.
In Ruby, I think what’s happening is that print is a method call.
We’re not giving it any arguments, so it prints nothing. It returns
nil, which we use as the initial value for a new variable named puts. Then
Ruby does something weird:
since you can never(?) call variables like functions, the expression
puts(404) is treated as a call to the global function named puts,
not a use of the local variable named puts.
So when we call puts(404), we’re really still
calling puts with 404!
But I don’t know Ruby, so take that explanation with an enormous grain of salt, please.
In both Python and Ruby, non-empty strings are truthy, and and
is a low-precedence operator that evaluates to its right-hand operand
if the left-hand one is truthy (which in this case it is).
I lazily lifted the Brainfuck representation of 52, >+>+>+[>+[-<+++>]<<]>,
from Esolang’s page on Brainfuck constants.
I wonder how challenging it would be to write a Brainfuck “superoptimizer”
for producing specific tape configurations (e.g. 52 48) or specific
ASCII outputs (e.g. 404). It seems that there are reasonably well-known
algorithms for producing single numbers in isolation, but I predict that
the shortest program to produce 52 48 on the tape, for example, ought
to be shorter than the sum of the lengths of programs to produce 52
and 48 independently.
Esolang has a page on Brainfuck code generation, but it’s not obvious to me whether any of the links on that page are relevant to my paragraph above.
