I wrote this node because jdrago999 asked me the other day: what's the difference between Acme::EyeDrops 👁️💦 and Acme::Bleach 🧴?
After giving a basic answer in that thread, I thought it would be fun to further chronicle the events behind these two concoctions, so as to give the reader some (hopefully interesting and instructive) insights into how on earth these two bizarre modules made it onto the CPAN.
I well remember what a sensation Acme::Bleach caused when it was first released way back in 2001. People were astonished and bewildered when their script disappeared before their eyes and yet still appeared to work! Impossible! Surely Conway was some sort of magician.
I'm not aware of Damian performing any magic tricks, at least I couldn't find any magic shows on offer at Thoughtstream. Yet he did pull a new and surprising idea out of his hat, namely to encode an entire source code file using just spaces and tabs. After all, code is just ones and zeros, so why not encode all the bits in each source code byte as space (zero) and tab (one)? Then later decode this unorthodox bitstream by converting space back to zero and tab back to one. And then, finally, eval the decoded string, thus resurrecting the original program. A simple and (in hindsight) "obvious" idea.
Yet why on earth was he thinking about such a strange concept in the first place? Well, it seems it was all Tim Maher's fault! Damian described how it came about when I asked him about it last week:
Acme::Bleach was conceived during a car ride with Tim Maher in Seattle. I believe we were on our way either to or from a Seattle.pm meeting at which I spoke.
We were talking about Perl Golf and he challenged me to create the shortest possible program for some task I can't remember. I almost immediately realized that I could make a visibly zero-length program by encoding everything as whitespace. By the next day the module was created.
I'm not aware of any earlier work in the whitespace programming domain, though it would not surprise me if there were some. It's an obvious steganographic technique, and has probably been employed in clandestine communications from the earliest uses of digital text. However, I can confirm that I was not (consciously) influenced by any other work when inventing Acme::Bleach. It leapt fully formed into my brain (as so many of my other ideas do). My unconscious mind is clearly vastly smarter and more creative than my meagre consciousness. ;-)
In terms of programming language history, it seems that Bleach was the first to use whitespace alone as a programming language, appearing a full two years before the Whitespace programming language.
Though not the first Perl joke module (see The Lighter Side of Perl Culture for that), Acme::Bleach was pioneering in other respects too: it was the first Acme module, and the first source filter joke module.
As testimony to its enduring influence, there are now more than 400 Acme modules on the CPAN and Acme::Bleach alone has spawned more than twenty variations and tributes over a period of ten years (see "Related CPAN Modules" section below).
Though there had long been joke modules on the CPAN, D'oh by Chris Nandor and Sex by Mick Schwern for example, it wasn't until Acme::Bleach was released in 2001 that joke modules became so popular that they demanded their own namespace. Indeed, it was Damian who came up with the name Acme:
to make it clear when a module was for entertainment purposes only, and was likely to lead to great harm to an unwitting user. Thinking of the many misadventures of Wile E Coyote the obvious namespace for such modules was Acme.Great idea. Yet, alas, it hasn't stopped people employing Acme modules in production code.
The Acme::Bleach Imitators
In the same year that Bleach was released, four imitators closely followed, as shown below:
Date | Module | Author | Country |
---|---|---|---|
Feb 21 2001 | Acme::Bleach | Damian Conway | Australia |
Apr 1 2001 | Acme::Buffy | Leon Brocard | England |
Apr 3 2001 | Acme::Pony | David Cantrell | England |
May 19 2001 | Acme::Smirch | Jasvir Nagra | New Zealand |
Sep 22 2001 | Acme::EyeDrops | Andrew Savige | Australia |
Humour is somewhat culture bound, so I find it interesting that the first five of these type of joke modules came from three countries with a similar cultural background, namely Australia, New Zealand and England.
I recall from private communication with Jas Nagra that he hacked Acme::Smirch to relieve the boredom during a long flight from New Zealand to America.
I assume that Bleach was a huge hit at the lively and mischievous London.pm, which probably explains Acme::Buffy and Acme::Pony. If Leon or David are listening, I would love to hear about how these two came to be created.
How did Acme::EyeDrops come about?
While Acme::Bleach was an original work, created in a day, Acme::EyeDrops was essentially a derivative work, created over a period of many months, and tinkered with for many years.
Around that time I was certainly aware of Acme::Bleach and its cousins. I remember showing my workmates how I could make their programs "cleaner" with Acme::Bleach. And fondly remember the look of disbelief on their faces when they realised their "empty" program still worked! Some of them were angry that I had "lost" their hard-earned code.
That said, the primary provocation for creating Acme::EyeDrops was not Bleach, but a thread on the (then lively, now dormant) "fun with perl" mailing list about JAPHs without any alphanumeric characters.
Following on from that thread, I subsequently released Acme::EyeDrops to "prove" that you could, in principle, go further than JAPHs and write any Perl program without using any "unsightly" alphanumeric characters at all. I was not the first to do this, however. Jasvir Nagra deserves the credit for being the first when he released Acme::Smirch in May 2001.
I was new to Perl at that time, and eager to improve my Perl skills by creating my first CPAN module. So I was happy to spend a lot of time on EyeDrops. In particular, I wanted to cleanly define the (sightly) encoding (see the "Sightly Encoding" section in the Acme::EyeDrops documentation) and further develop some original shape pouring algorithms.
How does Acme::EyeDrops differ from Acme::Bleach?
Both EyeDrops and Bleach encode the source code. Bleach does so by converting the stream of bits in each source code character to spaces (0) and tabs (1) as indicated by this snippet from its source code:
In contrast, EyeDrops encodes each "unsightly" (i.e. alphanumeric) character by using a bitwise operator on two or more "sightly" (i.e. non-alphanumeric) ones. This is done via a lookup table as indicated by this snippet from its source code:sub whiten { local $_ = unpack "b*", pop; tr/01/ \t/; ... }
my @C = ( q Z('!'^'!')Z,q Z('('^')')Z,q Z('<'^'>')Z,q Z('>'^'=')Z, q Z('>'^':')Z,q Z('>'^';')Z,q Z('+'^'-')Z,q Z('*'^'-')Z, q Z('+'^'#')Z,q Z('*'^'#')Z,q Z('!'^'+')Z,q Z('!'^'*')Z, q Z('!'^'-')Z,q Z('!'^',')Z,q Z('!'^'/')Z,q Z('!'^'.')Z, q Z('?'^'/')Z,q Z('<'^'-')Z,q Z('-'^'?')Z,q Z('.'^'=')Z, q Z('+'^'?')Z,q Z('*'^'?')Z,q Z('?'^')')Z,q Z('<'^'+')Z, q Z('%'^'=')Z,q Z('&'^'?')Z,q Z('?'^'%')Z,q Z('>'^'%')Z, q Z('&'^':')Z,q Z('<'^'!')Z,q Z('?'^'!')Z,q Z('%'^':')Z, q Z('{'^'[')Z,q Z'!'Z,q Z'\\\\'.'"'Z,q Z'#'Z, q Z'\\\\'.'$'Z,q Z'%'Z,q Z'&'Z,q Z"'"Z,q Z'('Z,q Z')'Z, q Z'*'Z,q Z'+'Z,q Z','Z,q Z'-'Z,q Z'.'Z,q Z'/'Z, q Z('^'^('`'|'.'))Z,q Z('^'^('`'|'/'))Z,q Z('^'^('`'|','))Z, q Z('^'^('`'|'-'))Z,q Z('^'^('`'|'*'))Z,q Z('^'^('`'|'+'))Z, q Z('^'^('`'|'('))Z,q Z('^'^('`'|')'))Z,q Z(':'&'=')Z, q Z(';'&'=')Z,q Z':'Z,q Z';'Z,q Z'<'Z,q Z'='Z,q Z'>'Z,q Z'?'Z, q Z'\\\\'.'@'Z,q Z('`'^'!')Z,q Z('`'^'"')Z,q Z('`'^'#')Z, q Z('`'^'$')Z,q Z('`'^'%')Z,q Z('`'^'&')Z,q Z('`'^"'")Z, q Z('`'^'(')Z,q Z('`'^')')Z,q Z('`'^'*')Z,q Z('`'^'+')Z, q Z('`'^',')Z,q Z('`'^'-')Z,q Z('`'^'.')Z,q Z('`'^'/')Z, q Z('{'^'+')Z,q Z('{'^'*')Z,q Z('{'^')')Z,q Z('{'^'(')Z, q Z('{'^'/')Z,q Z('{'^'.')Z,q Z('{'^'-')Z,q Z('{'^',')Z, q Z('{'^'#')Z,q Z('{'^'"')Z,q Z('{'^'!')Z,q Z'['Z, q Z'\\\\'.'\\\\'Z,q Z']'Z,q Z'^'Z,q Z'_'Z, q Z'`'Z,q Z('`'|'!')Z,q Z('`'|'"')Z,q Z('`'|'#')Z, q Z('`'|'$')Z,q Z('`'|'%')Z,q Z('`'|'&')Z,q Z('`'|"'")Z, q Z('`'|'(')Z,q Z('`'|')')Z,q Z('`'|'*')Z,q Z('`'|'+')Z, q Z('`'|',')Z,q Z('`'|'-')Z,q Z('`'|'.')Z,q Z('`'|'/')Z, q Z('['^'+')Z,q Z('['^'*')Z,q Z('['^')')Z,q Z('['^'(')Z, q Z('['^'/')Z,q Z('['^'.')Z,q Z('['^'-')Z,q Z('['^',')Z, q Z('['^'#')Z,q Z('['^'"')Z,q Z('['^'!')Z,q Z'\\\\'.'{'Z, q Z'|'Z,q Z'\\\\'.'}'Z,q Z'~'Z,q Z('!'^'^')Z ); push @C, map(join('.', q#'\\\\'#, $C[120], map($C[$_], unpack('C*', sprintf('%x', $_)))), 128..255); sub ascii_to_sightly { join '.', map($C[$_], unpack('C*', $_[0])) }
The big difference between the two approaches lies in the decoding. Because EyeDrops encodes into a form that perl can execute unaided, it simply lets perl decode it via eval and so does not require Acme::EyeDrops to be installed on the target system.
In contrast, perl by itself cannot make sense of a raw stream of spaces and tabs and so requires Acme::Bleach to be installed on the target system to decode and eval the "bitstream" of spaces and tabs.
As a consequence, Bleach has to be involved at compile time (which is why it overwrites the file and fiddles with $0). EyeDrops, on the other hand, does not mangle the original file, simply reads it and writes a new (encoded and shaped) one.
Put another way, Bleach is a source filter while EyeDrops is not. EyeDrops is simply an encoder and shaper of the bytes in a source code file. The hardest part of writing EyeDrops was not the (sightly) encoding, but the heuristics required to pour the encoded bytes into a wide variety of shapes.
Perl Monks References
- unbleach.pl
- Unlocking the Dark Mysteries of Acme::Bleach
- Most fascinating but *not really* useful modules
- 60 Acme Modules
- The Lighter Side of Perl Culture (Part I): Introduction
- Production obfuscation
The two excellent nodes below describe a "converse" encoding to the sightly encoding employed by Acme::EyeDrops. That is, they encode a Perl program to use only alphabetic characters, without any punctuation at all:
- Yet Another Obfuscation Engine by mtve (2002)
- Re: Re: exactly 390 bytes by ambrus (2004)
Update: some others:
- No more $$$ by mtve
- Yet Another**2 Obfuscation Engine by mtve
- Yet Another**3 Obfuscation Engine by mtve
- Fun With Reserved Keywords by blokhead
- exactly 390 bytes by anonymous
- Ode for getprotobyname by ambrus
- Indigo japh by ambrus
- ppencode by dankogai
External References
- Advanced Perl Programming by Simon Cozens, chapter 10, Fun with Perl
- Source Filters in Perl 2010 by Beatnik aka hendrikvb aka Hendrik Van Belleghem
- Acme::EyeDrops in JPerl Advent Calendar 2009 (Japanese)
- Whitespace programming language
- ProleText Specification by Brad Templeton
Related CPAN Modules
- Acme::Bleach by dconway (Feb 21 2001)
- Acme::Buffy by lbrocard (Apr 1 2001)
- Acme::Pony by dcantrell (Apr 3 2001)
- Acme::Smirch by jnagra (May 19 2001)
- Acme::EyeDrops by asavige (Sep 22 2001)
- Acme::Buckaroo by kevinrice (Jun 6 2002)
- Acme::Code::Police by ovid (Nov 19 2002)
- Acme::Code::FreedomFighter by gmccar (Nov 21 2002)
- Acme::DoubleHelix by xern (Dec 7 2002)
- Acme::Floral by jjore (Jul 3 2003)
- Acme::Bushisms by accardo (Jul 13 2003)
- Acme::Lingua::NIGERIAN by jwalt (Aug 6 2003)
- Acme::DeepThoughts by podmaster (Aug 25 2003)
- Acme::DonMartin by dland (Apr 8 2005)
- Acme::PerlML by adamk (Aug 17 2005)
- Acme::Bleach::Numerically by dankogai (Aug 30 2005)
- Acme::ScriptoPhrenic by dmuey (Nov 13 2005)
- Acme::Homer by dmuey (Dec 18 2005)
- Acme::Enc by dmuey (Dec 31 2005)
- Acme::Greek by jrockway (Apr 3 2006)
- Acme::Phlegethoth by rcaputo (Jul 3 2006)
- Acme::ChuckNorris by dmuey (Nov 24 2010)
- Filter::Util::Call by pmqs (Aug 28 1995)
- Lingua::Romana::Perligata by dconway (Dec 7 2000)
- Filter::Simple by dconway (Feb 21 2001)
- DNA by mschwern (Apr 1 2001)
- SuperPython by mjd (Apr 1 2001)
- Filter::CBC by beatnik (Nov 2 2001)
- Acme::AsciiArt2HtmlTable by cog (Jul 26 2005)
- Acme::AsciiArtinator by mob (Nov 3 2009)
References Added Later
- My Year in Perl, 2004 edition by brian_d_foy
- Most fascinating but *not really* useful modules by monkfan
- Most Interesting Modules by smalhotra
- Format your source code into arbitrary shapes by CubicSpline
- Re: Polygon Creation -- Request for Algorithm Suggestions
- Re: too much free time (99 Bottles of Beer)
- Perl 5.17 change to use re 'eval' breaks Acme::EyeDrops
- Re^4: Pascal-Sierpinski for golfers
- Re: clean output from Deobfucation
- Properties, metadata and Perl celebrities
- Re: Programs that write programs by moritz
- Form Before Function : Perl as visual art medium. by luis.roca
- Acme Bleach by sameerperl
- Re: poll ideas quest 2007 by moritz
- An awful, horrible, evil idea.. by dynamo
- how to encryption script by anonymonk
- 60 Acme Modules by cog
- Can someone please break this obfuscated code down for me by axl163
- too much free time by anonymonk
- the star still shines by shmem
- 99 Bottles of Beer (Language Perl)
- Making a point on LinkedIn about Perl; by ait
- Ressources for Acme namespace cultural implications by Smonff
- Re^2: Stupidest Prime Number detector ever!!
Updated 26-Apr-2012: Corrected release date of Acme::Pony (originally Pony) to Apr 3. Thanks DrHyde. 28-Apr-2012: added DNA module by Schwern and SuperPython module by MJD to "Related CPAN Modules" section. 2-May-2012: Minor improvements to wording. Dec-2019: Added References Added Later section.
|
---|