http://qs321.pair.com?node_id=967004

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:

DateModuleAuthorCountry
Feb 21 2001Acme::BleachDamian ConwayAustralia
Apr 1 2001Acme::BuffyLeon BrocardEngland
Apr 3 2001Acme::PonyDavid CantrellEngland
May 19 2001Acme::SmirchJasvir NagraNew Zealand
Sep 22 2001Acme::EyeDropsAndrew SavigeAustralia

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:

sub whiten { local $_ = unpack "b*", pop; tr/01/ \t/; ... }
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:
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

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:

Update: some others:

External References

Related CPAN Modules

References Added Later

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.