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.