(upbeat music)
- All righty, everyone, I think
we're about ready to start.
So, hi.
I want to welcome you to That Works?!
Quines and Other Delightfully
Useless Programs.
By the end of this talk,
I'm hoping that all of you
will be able to look at the
background of this slide
and say, "Of course!
I know what that does.
"It's incredibly obvious, I mean, lookit!
"It's Ruby, that is
valid Ruby, so obviously
"it's all very simple.
"Ruby is such a readable
language that you can
"write code like that, it's great."
So my name's Colin Fulton.
I'm a front-end developer
for a security company
called Arbor Networks.
If you wanna get in touch
with me, I'd recommend
emailing me, 'cause I
only just joined Twitter,
and I actually haven't
tweeted anything yet.
But if you wanna see stuff
like I do in this talk,
I'd recommend following
me on Twitter, though,
'cause I'll be posting more stuff later.
Also, all of the code
that you see in this talk
that I personally wrote
is available on GitHub.
Some of it actually just
uploaded, and it's all
under open-source licenses,
so if you want to use this
in your production code,
you can use it for free.
It's great.
(whispers) Don't do that.
(laughs softly)
So I want to start this
talk with two stories
about playing with computers.
The first is in the 1830s.
Charles Babbage was working on a problem,
and got Ada Lovelace, whose
full name is that long name,
which is why we all call her Ada Lovelace.
They were working on this problem.
Back in the time, if
you wanted to do math,
you didn't have computers,
you didn't have calculators,
and so if you wanted to
do lots of calculations,
you'd often use log books.
And all these books are
are tables of logarithms
of different numbers together.
Lots and lots of tables and numbers.
The problem is, if you
don't have computers,
how do you calculate a giant
book full of logarithms?
And the answer is, you
calculate it by hand.
Hundreds and hundreds of pages
of thousands and thousands,
or tens of thousands, of calculations.
As you can imagine, a couple errors
creeped in there, over time.
Now, if you're doing
astronomical calculations,
this isn't too bad, 'cause
you're just doing science,
and whatever, if there
are inaccuracies in there,
it doesn't affect anyone.
But if you're doing naval computation,
or military computation,
this kind of thing
could get important.
And so the errors in the log
books were a major issue.
Charles Babbage tried to figure
out an engineering solution
to this problem.
Now he never actually built the
device that he came up with,
but we do have this,
the beautiful fragment.
Just a segment of a
machine that he was trying
to create and design.
What this is, is it's a mechanical device
made out of steel and brass,
and you input some initial
numbers into it, and you
turn a crank on the machine,
and the full-sized machine
would run through all
of the calculations mechanically,
and unlike a slide rule,
it actually would do exact calculations.
If a wheel was off by just a half turn,
the entire machine would jolt to a halt,
so it wouldn't make mistakes.
But of course, if you're just
gonna calculate the numbers,
that isn't good enough,
because those numbers
have to be copied by
printers, and laid out
to actually print on a printing press.
And so the full-sized
version of this machine
had designed into it an actual printer,
that mechanically would
lay out all of the numbers,
and in future versions of
it, could actually let you
adjust the kerning and the
spacing, and the layout
of that table.
He never got to build this,
'cause as you can imagine,
this was a very, very
difficult device to design
in the 1800s, and Charles
Babbage was a horrible,
horrible human being to work with.
He's just monstrous and
terrible, and didn't
give any respect to the
person who was actually
manufacturing this for him.
And so, while he was
working on this, he came up
with some ideas, and
since the project ended,
he ended up trying to work on those ideas,
and came up with the idea
of the analytical engine.
He thought, "Why would you have a machine
"whose sole purpose is just
to calculate log tables?
"That's very useful for this one case,
"but we don't want to
make a separate machine
"for every problem."
So the idea of the analytical
engine, it would be
a giant machine built
out of steel and brass,
which would actually
calculate generic programs.
This was the first real
general purpose computer
that anyone came up with.
It had the equivalent
of serial buses on it,
of registers, it had opcodes inside there,
it even had some microcode on the inside,
for optimizing certain operations.
This device was way too
complicated to build,
and he didn't fully comprehend
a lot of computer science.
Well, because it didn't
really exist back then.
For example, the registers
in the largest versions
of the machine that he had thought about
could store 100-digit numbers in them.
And that's not 100 binary digits;
that's 100 decimal digits.
No one needs that level of
precision in their programs.
But he thought it might
interesting that you could do it,
and because it was
general purpose, you could
just add more digits to the registers.
Now, this device was purely
made for crunching numbers.
Where Ada Lovelace came
in, she was helping Babbage
work on this, and was
writing some papers for him,
and she had an idea.
The analytical engine
might act upon other things
besides numbers, were
objects found whose mutual
fundamental relations
could be expressed by those
of the abstract science of
operations, computer programs.
Supposing, for instance, that
the fundamental relations
of pitched sounds in
the science of harmony
and of musical composition
were susceptible
of such expressions and
adaptations, the engine
might compose elaborate and
scientific pieces of music
of any degree of complexity or extent.
What Ada Lovelace is
noticing, here, is that if you
have a general purpose machine,
yes, it can only operate
on the numbers, but those
numbers can represent anything
that you can sort of
programmatically deal with.
That could be music, that
could be text, poetry.
She predicted the information revolution
before computers were invented.
Before the electric light was invented.
This was a wonderful,
wonderful observation.
But at the time, no one
really made anything of it,
because one, this device
was never made, and two,
why would you want a machine
that could compute music,
or calculate images?
That doesn't make any sense.
And as we know today,
it totally isn't useful,
and we don't all rely
on it every single day.
Sometimes toying around
with computers like this
can change the world.
Ada Lovelace wasn't
the only person to just
play around with computers and come up
with a world-changing concept.
The Linux kernel itself
was created by Linus
as a hobby project.
He didn't think it would go anywhere,
and in his original post,
he thought that he'd
only support one kind of
hard drive, 'cause that's
the only kind of hard drive that he had.
Now we know, today, the
Linux kernel obviously,
a little bit more popular and relied on
by a couple more people.
But that isn't where it
started; it started as just
playing around with code.
Ruby was created when
Matz just wanted to play
around with code.
The world didn't need
another scripting language,
it didn't need another
object-oriented language,
but Matz wanted to have a
little fun, and obviously,
we're all here today because he was just
toying around with code.
This talk is not about that.
This talk is about having
fun, but not that kind of fun.
It's more about this story.
In the 1970s, a man named
Steve Dompier was part
of the MIT Homebrew Computer Club.
Now, this is a club where
they just play around
with computers.
Now, in the 1970s, computers
couldn't do very much,
and he had just gotten
one of these machines.
There was a kit that you could
build called the Altair 8800.
As you can see on the
front, you have a bunch
of switches and lights, and
that's all that you have.
If you want to program this
machine, those switches
will let you individually
switch on signals
to each line of the address
bus, and using another switch,
you can individually pipe
that data into the machine,
line by line, on the address bus.
This is very time-consuming.
This isn't just writing assembly code,
this is writing machine code,
in binary, using DIP switches.
Not the greatest way to program,
but everything has to start somewhere.
Now, when he was playing
around with this machine,
he had a radio turned on
next to him at one point.
And he noticed that when
he ran certain programs,
little bleeps and bloops
would come out of that radio.
If you've ever had a Bluetooth speaker
with an iPhone near it,
you may have noticed that
while your iPhone is turned
on, a little bit of noise
comes out of it.
And the reason for that
is radio interference.
If you look on the back of
most electronic devices,
you'll see this symbol these days.
This is a symbol for the FCC.
They regulate what you can
do with electronic devices.
They make sure that, for
example, your computer
doesn't spew out lots of
electromagnetic radiation,
so that if someone's standing
nearby with a pacemaker,
their pacemaker doesn't fail.
They also ensure that
devices like pacemakers
aren't gonna be susceptible
to that kind of interference.
Well, back in the 1970s,
this wasn't as big a concern,
'cause computers weren't as prevalent,
clock speeds were a lot
slower, and so these devices
weren't very well shielded.
So what he was picking up
on that radio was the sound
of the computer making computations.
And he thought, "Huh, that's interesting.
"Let's play around with that."
So he went about in the
code and figured out
that if I access these
specific memory addresses,
I get these specific tones.
And I believe he pulled out his guitar,
and he checked what note that was,
and he said, "Oh, okay, that's a C."
He tried different addresses,
different operations,
came up with different tones,
and eventually wrote a program
that would play music via
the radio interference
coming off of his computer.
Nothing useful came out of this.
This is obviously just a toy
thing that he did for fun.
It's incredibly elaborate,
there's no point to it,
but he and his friends had a lot fun doing
this kind of thing.
Obviously, later, we just
added speakers to computers
and audio ports, 'cause
that's a much, much better way
to make music.
But this is programming
for programming's sake,
and that's what I want
to talk about today.
Programming just for the fun of it.
Not trying to make something
useful, not trying to make
an open-source project that's
gonna take over the world,
not trying to work on some
big production website.
No, it's just about
programming for the sake
of writing code, 'cause
we enjoy writing code.
Now there's good code, and
that's what we talked about
for most of RubyConf.
We talked about good code.
How to write code that's
efficient, that's performant,
that's easy to read.
And there's bad code.
We talked a bit about that.
We talked about how to write
code that is not inefficient,
that doesn't have all those aspects
that we associate with bad code.
But we're gonna talk about
fun code in this talk.
Now, fun code can be non-performant,
which would put it in
the class of bad code,
and it's completely
useless, which would put it
in the class of bad code, and it's usually
terrible in terms of
readability, as you'll see
from later slides in
this talk, but it's fun!
And more importantly, oops,
it's fun!
It's really fun.
This kind of code is
just a blast to write,
at least if you're the
kind of person who enjoys
just tinkering around and
doing problem-solving.
Now a warning: this talk contains really,
really ugly-looking code.
And I mean really ugly-looking code,
written in horrible ways.
So if any of you are
sensitive to bad code,
if any of you has recently
had bad experiences
on real projects, you
may want to leave now,
'cause this may give you flashbacks
to terrible, terrible times.
That being said, I hope
none of you have ever
seen code like this,
'cause this is my reaction
if anyone ever does any
of this in production.
But, let's just sit back, let's
relax, let's take some time,
and I'll explain how all this
really terrible code works.
So first I want to talk about
esoteric programming languages.
Now, esoteric programming
languages are languages
which aren't really for
practical purpose, but they
can usually compute anything
that any other language can,
and are either very difficult to write in,
or you just sort of play
around with interesting ways
to write programs, 'cause
there's more than just sort of
the standard C syntax
that we're all used to.
One of these is a
language called BrainFun.
Okay, I'm lying.
The language isn't
actually called BrainFun,
but sometimes children attend these talks,
so let's refer to it as BrainFun.
And those of you who
know Ruby in the audience
will understand what I'm talking about.
BrainFun only has these operations in it.
Each operation is only one character,
which makes our code nice and small,
and as you'll see here, there
are only eight operations.
And what they do is very simple.
BrainFun assumes that your environment
is an infinitely long table of integers.
And there's a pointer pointing
one of those integers.
If you use this operation,
it'll take that pointer,
and move it one over to the left.
This operation will move the
pointer one over to the right.
The increment operation takes
the number that that pointer
is pointed to, and increments it by one.
Decrement, on the other
hand, decrements by one.
Now, this isn't very
interesting, but we can use
the period operator to output whatever is
underneath the current
pointer into a stream
that the program is outputting
to, usually standard out.
You can use the comma
operator to take a byte
from the input string, often
something like standard in,
and put that where the current pointer is.
Now, these operations
alone do not give you
a Turing complete langauge.
They can't compute
everything that you can do
with any other language.
So we need two more operations.
And these are these, these
square bracket operators.
The first square bracket
operator looks at the number
that you're currently
pointing to, and checks,
is it equal to zero?
If it is equal to zero,
it jumps in your code
to the matching square bracket operator.
If it doesn't equal zero, it's
gonna go ahead and jump to,
it's just gonna go ahead and continue
to evaluate the program.
And so you can write
loops where you iterate
on a bunch of numbers, and
then zoom back to a number,
decrement it down by one, and
keep going over that code,
until eventually that number hits zero,
and then you can jump ahead and execute
the next part of your code.
Now, that's all you need
to do all computation.
In fact, someone wrote a
BrainFun interpreter in BrainFun.
I'm pretty sure, if I
remember correctly, you can't
implement the BrainFun interpreter
in the BrainFun interpreter,
but with some modifications,
you can go ahead and do that.
Another example of an
esoteric programming language
is JSFun, and again,
it's not called JSFun,
but let's all pretend it's called JSFun.
JSFun is a different kind
of esoteric language.
It was created as a parody of
a language which we all like,
or some of us like, called JavaScript.
JavaScript is an interesting language,
because it does type juggling.
You can actually change between a lot
of the different types in
the language implicitly,
by performing certain operations.
So JSFun takes advantage of that.
It started when a couple of
programmers on a forum wondered,
"How few characters do we need
"to write any JavaScript program?"
And I don't mean a Turing
complete subset of JavaScript,
I mean how can we represent
any program in JavaScript
using a subset of the
characters that we normally
use to write code.
So we can evaluate strings as
if they're code in JavaScript.
So if we call the function
prototype and pass on a string,
it'll evaluate that as if it's code.
Okay, so we're still
using all the characters,
but this gets us one step closer.
We don't need all of
those letters, though.
We can take numbers, which are the numbers
that correspond to the ASCII values
for every single letter in the alphabet,
and we can turn those
numbers into strings,
and then concatenate
those strings together.
So now we're only using
a couple of letters,
enough to get us the function prototype,
the string prototype, and
the fromCharCode function,
and then we use parens, the dot to call,
the plus to concatenate things together,
and then all of the numbers.
So now we can get pretty
close to having a much, much
reduced subset of JavaScript.
But look at all those numbers.
Do we really need, like,
all of the numbers?
I mean, zero through
nine, doesn't that seem
a little bit, kinda, redundant?
Instead, we can just use
one, and just add on one
as many times as we need.
Now, we also need zero to represent zero,
but luckily, we're not gonna
need that in most cases.
So this will get us
closer, and now we have
eliminated all those numbers.
Now, the rest of the
process of getting to JSFun
starts getting a little bit esoteric.
These are all of the symbols that you need
to represent any JavaScript program.
Six characters.
Six characters, and you can
translate any JavaScript program
into a completely valid JavaScript program
that will run exactly the same code
in essentially any browser,
except for some quite old ones.
So how do we do it with
these six characters?
This is an empty array.
This makes sense.
Is everyone following? (laughs softly)
'Cause this is gonna get confusing.
If we apply the NOT
operation to an empty array
in JavaScript, an empty
array evaluates a truthy,
and so NOT, to a truthy
object, is gonna be false.
Okay.
If we apply the NOT operation
twice to an empty array,
we're gonna get true.
This makes sense.
The plus operation isn't just
addition between two numbers.
There is also a unary plus
operator, which implicitly
converts whatever you
give it into a number.
So our statement that
we already have is true,
and true turned into a
number is the number one.
Now we have a number,
without using any numbers.
(audience laughs)
How do we get strings?
Obviously this is how we get strings.
JavaScript is a very intuitive language.
So as we all know, if
you take an empty array,
and add it to another
empty array, what it does
is it performs the join
operation on both arrays,
which empty arrays joined
together with empty strings
turns into empty strings,
and then you join together
empty strings, and then you
get another empty string.
Very intuitive, makes a lot of sense.
If we index into an empty
array using an empty array
as a key, JavaScript is gonna
try and look up a property
or a function on the empty array,
which is called empty array,
which it doesn't have.
And so it returns undefined.
So now we have undefined,
which isn't very useful,
except if we add an empty array onto that,
we get the string undefined.
Now we have all of the
letters in undefined.
(audience laughs)
This process carries on
for a very long time,
and if I remember correctly,
the letter capital C,
under this way of doing things,
takes about, I think it's 1,800 characters
of code to generate.
But going through this
process, we can eventually get
all of the characters that we need,
we can concatenate them together,
and using some other tricks,
we can get that function
prototype that we got,
and call everything with
the function prototype.
And so now we have all of JavaScript
in only six characters.
These sorts of esoteric
programming languages
are nothing new.
In 1972, two Princeton
students who were feeling
particularly fresh, came up with their own
programming language to make fun of the
programming languages of the time.
It was called INTERCAL.
INTERCAL obviously stands
for Compiler Language
With No Pronounceable Acronym,
because that's the way
they rolled in the 1970s.
They liked to do a number of
things with this language,
including have a bunch of new operators,
one of which was explained with a massive
boolean logic table, and
just said, "Obviously,
"this is how it works."
The compiler also had a number of features
which were undocumented,
'cause that's everyone's
favorite kind of compiler
feature: an undocumented one.
This one would raise an
error if you didn't put
the Please keyword enough
times in your program,
saying that your program
wasn't polite enough.
(audience laughs)
But if you put in too many
pleases, it would also
raise an error, 'cause now
you're just being cloying.
(audience laughs)
Now, you don't have to make an
esoteric programming language
that's hard to read like all of these.
And so I came up with
an idea at one point,
and here's what it was.
How many are familiar with SASS?
All right, so pretty much everyone.
SASS is a CSS preprocessor, which you're
probably familiar with.
It allows you to have things
like variables and functions
inside your CSS, so you
can dry up your CSS.
SASS is a Turing complete language,
so here is an implementation
of Fibonacci using SASS,
which, everyone knows that
you need to put Fibonacci
inside of your CSS, 'cause
everyone writes CSS like that.
And you probably are
all familiar LISP now,
'cause we talked about
it in the initial talk,
but for those of you who aren't,
LISP is a family of programming languages.
It isn't an individual
programming language.
The main idea of LISP is that
you represent your program
as a series of nested lists.
Those nested lists are
represented by using parens
to enclose your lisp.
The first item in the lisp is usually,
but not always, interpreted
as the name of a function,
and then the rest of the items of the list
are the arguments of that function.
So you can chain together
a bunch of things.
So here, in a very primitive form of LISP,
we're defining a global variable,
we're calling it lisp_fib,
we're assigning a lambda,
anonymous function.
That, and we're doing
the same implementation
of the Fibonacci function
we showed in SASS, in LISP.
But you know, SASS also
lets you write lists
with just parens and
spaces between things.
And you don't have to put
quotes around strings.
So I created Sassy LISP.
(laughs softly)
This is the exact same LISP
program, but written in SASS,
passing into a function
called function_eval.
Now, we don't have time
to go into the details
of this, but this is about a
50- to 70-line implementation
of LISP, inside of SASS.
And then there's another 50
lines of helper functions.
You can define anonymous
functions, you can create
first-class functions and
compose them together,
you can have variable scoping with global
and local variables, the lambdas
actually act as closures,
they close around all the
local variables defined
at the time that they were created.
And in addition to all of that,
you also get anonymous functions.
SASS itself doesn't have
anonymous functions,
but because SASS is Turing complete,
we can implement anonymous
functions inside of SASS.
Don't use this in
production code, 'cause one,
all of your co-workers
will think you're crazy,
and two, this is very inefficient.
SASS isn't the best runtime
for implementing a language.
But you can do it,
(audience laughs)
so why not?
And this is on GitHub if you
want to play around with it.
I also have a benchmark.
You can run this Fibonacci
program up to about 15 or 14,
and around there, the
stack eventually blows
after it sits there for a
while, attempting to compute
lots of lots of inefficient code.
It might run a little bit
faster if I implemented
tail call recursion, but
I'm not gonna do that
until I get really bored,
so give me, like, a week.
(laughs softly)
All right, so now we're gonna move on
to something completely different.
Who can tell me why the map
function is better than each?
Anyone?
- [Audience Member] It's
one character shorter.
- Exactly!
(audience laughs)
Map is better than each because
it is one character shorter.
We're gonna be talking
about code golf now.
(audience laughs)
Code golf is the game of trying to write
a given programming prompt in
as few characters as possible.
Let's try playing around.
Here's FizzBuzz.
FizzBuzz is a very simple program.
You're gonna print out the numbers
between zero and 100 on standard out.
Very easy to do, except
you're gonna replace
numbers that are divisible
by three with the word Fizz,
numbers that are divisible
by five with the word Buzz,
and numbers that are divisible by both
with the word FizzBuzz.
Here is a very simple
implementation of that in Ruby.
We use the modulus function
to check whether or not
things are divisible by certain numbers,
and then we output various strings.
All right, this is not
a great implementation,
but don't worry, 'cause
we're about to write
an even worse implementation.
So the first thing that
you wanna do in code golf
is look for repeated things.
Patterns that are going on multiple times.
In this case, we're calling
puts multiple times, there,
and we don't need to do that,
because the if statement
is gonna return a value.
So we can pull out that puts,
and put it on the end, there.
Okay, that's good.
But now we have all of these
repeated equality checks,
and we're never gonna
get a negative number
out of this function, so
why don't we just check
if the number, not if it's equal to zero,
but if it's less than one.
That will do an equivalent
thing for this bit of code.
And we just saved three characters.
Pretty good.
If we use case statements, we can remove
those repeated calls to
the modulus function,
and dry up our code
just a little bit more.
And this is good, but we
still have case, when,
and sorry, there's a little typo in there,
but you still have all these case and when
in the else block.
And so, we can do better than this.
This is not exactly the
easiest read to thing,
but instead of using case
statements we're using a ternary.
We're checking if the number
is divisible by three.
If so, we're gonna return the word Fizz.
If not, we're gonna
return an empty string.
Then we can concatenate that on to
doing the same thing with Buzz.
Then we're gonna check
to see if that's empty.
If it is empty, we're gonna
turn the value that we got,
if not, we're gonna turn the output,
'cause it says either
Fizz, Buzz, or FizzBuzz.
This is pretty good, but we can do better.
This refactor is a little
bit more confusing,
so let's take a closer look.
We have an array which
only has one item in it,
the string Fizz.
We then index into that by
calling modulus on the value
with three, so if the
value is equal to zero,
which is what we want to check,
it'll get the first item out
of that array, which is Fizz.
If not, it's gonna return nil.
And so if we put our
empty string in there,
it's not a little bit
shorter, but this actually
lets us do this next refactor,
which is totally readable.
This code is getting better,
right? (laughs softly)
(audience laughs)
All righty.
So we're gonna have the same
thing that we had before,
except now, inside of
that string with Fizz,
we're going to do string interpolation.
And inside of there, we're
gonna put the same thing,
but with Buzz.
So now if this doesn't return
Buzz, it's gonna return nil,
and in string, doing string interpolation
with the value nil,
gives us an empty string.
That's pretty close to what
we want, and so this is most
of the way there, because,
see, there's an empty space.
The problem, here, is that
if it's divisible by five,
but not divisible by three,
the Buzz value is gonna get thrown away,
and the whole thing's gonna return nil.
So if we just put a
little equality statement
inside of our string interpolation,
or on the outside of that, we can pull out
that little bit of code.
So then we have this.
But now that puts with the
output.empty at the bottom,
we don't need any of that.
Instead we can just put the
value on the end of that or,
and then we have, like,
multicharacter variable names,
and I know I personally hate those,
and I know everyone else hates
multicharacter variable names
'cause they make your code so readable.
So let's get rid of those.
We're using do endblocks,
which is idiomatic Ruby, here,
but that takes up extra
characters, so we don't do that.
And actually, let's
make it idiomatic Ruby.
Let's just remove all that whitespace
and put it on one line.
So here we have FizzBuzz.
Our original implementation,
including whitespace,
was 171 characters, and
our new implementation,
with that one little bit
of whitespace in there,
is a total of 56 characters.
If you can write a shorter
implementation of FizzBuzz,
I'd really like to see it.
I'm sure that there is
one in Ruby, but this is
getting to the point where any
bit of code that you delete
is gonna make invalid
Ruby syntax very fast.
Another way that you can
have fun with programming
is by doing obfuscation.
The International
Obfuscated C Code Contest
is a great example of people
just writing obfuscated C code.
And if you thought that
C code was hard to read,
you should see what these
people come up with.
They are beautiful, and I mean
beautifully ugly programs.
Yusuke Endoh is one of the
core maintainers on Ruby,
and he is a master of obfuscated code.
He has won the IOCCC multiple times,
including with this one,
called The Most Overlooked Obfuscation.
And this one we have to run
because if you're a C developer,
this is a good one to see.
So we're gonna go ahead
and cat our program,
and now it's a C, we have nt main,
so that's our main function.
We're then gonna call print f,
which prints a standard out,
and print out "Hello world!"
with a newline character.
This seems like it makes sense.
Now you're not including
standard out, here.
That may confuse some of
you C developers out there,
but in most environments, you can do that.
It's okay.
The compiler will throw
a warning to tell you
you're doing something terrible
that may not always work,
but it works often enough.
So this should be fine.
So we're gonna go ahead and we're gonna
compile this program, so we'll do gcc,
we're gonna turn off
warnings, because we don't
need to see that warning
message, and then we are gonna
output it to a file,
which we'll call prog.
Runs for a moment, and
oops, I actually have to
give it a program.
They really need to fix that in gcc.
They should just assume
that they know what program
I want to run.
Compiles for a moment, and
then we go ahead and get that.
Okay, so now we have a
program, and so to run it,
we will go ahead and run it.
And it printed "Hello,
world!" to the screen,
along with the rest of
the program. (laughs)
Now, look at the initial line up there,
look at the final line,
it's the same code,
and that's what it output.
Okay.
If you are confused, good.
(audience laughs)
I will make things a little more confusing
by viewing this program in less.
Now for those of you familiar with less,
it prints out code to
the screen, it lets you
scroll through it, but it
doesn't add code highlighting.
So why is there an underscore
under "Hello, world!"?
Okay, I want to find out what's going on,
so let's open this up in an editor.
Okay. (laughs)
(audience laughs)
What Yusuke Endoh did is he
encoded some non-printable
characters into his code.
Specifically, he printed out a character
which exists in ASCII, which
deletes the previous character.
So he wrote out a much
longer program which,
when you print it to standard out,
is going to remove all of
that fluff that actually
runs the real code, and it's just gonna
show you a fake program.
When gcc evaluates this,
it ignores all the escape characters,
and goes ahead and prints it.
And so that is the most
obfuscated bit of code.
All righty.
(audience applauds)
Now we're gonna move on to my favorite
kind of obfuscated program, the quine.
The rules of quines are quite simple.
Your program, when it runs,
must print out its own source code.
This is relatively easy,
if it weren't for rule two:
the program can't actually
read its own file.
Let me show you why that's a problem.
When we want to print
something out in Ruby,
we use puts, and then we give it a string.
Okay, so we want to print
out our own program.
Well, if you run this,
it's just gonna print out
an empty string, so
let's put in the program.
Well, the program starts with puts,
so I'm gonna put that
in the string, and then
I'm gonna run that, and
okay, it output the beginning
of the program, but it
didn't output that string
that we wanted to output.
All right, so what I'm
gonna do is I'm gonna
escape the quotes,
because we need to escape
the quotes inside there, and I'm gonna
put puts inside of that.
And now we have a program that does return
the previous program,
except now the program's
a little bit longer.
But we can fix this.
If we just keep escaping strings,
and keep adding puts,
infinitely, eventually we will
reach our program towards the limit.
Unfortunately, Ruby doesn't let us
write infinitely long programs.
This isn't gonna work.
So we're gonna have to be
a little bit more clever.
We're gonna assign the
string with puts of it
to a variable, and then output that.
Now, if we run this, it's
still just gonna output puts s.
But, we have the actual
code of the program
inside of a string, so we
can call eval with that.
So we'll say eval s equals that string.
And we run that, it's still
gonna output the same thing,
but you'll notice that
we're evaluating code, now,
which outputs the variable s.
And that variable s is
contained within the string.
So we actually have reference
to that string itself
inside of the string that we're outputting
and evaluating as code.
So what we can do is we can add a string
that adds the eval s onto the output.
We get one step closer.
Now we have most of the program, but we're
missing those quotes around the outside.
Now, if we try and just
escape those quotation marks,
we're gonna run into the
same problem we had before,
where we keep having to
escape characters endlessly,
and it never works.
But we can use the dot chr
method on integers in Ruby,
which allows us to take an integer
and turn it into an ASCII character.
In this case we need the character 34,
and if we run this, we now have a quine.
This is a valid Ruby program
which, when you run it,
outputs its own source code.
And if you run what it output in Ruby,
it'll then go ahead and return itself.
Hurray! Woo!
Quines, I like to think
of them as the fugues
of esoteric programming.
Fugue is a very rigid
structure in classical music.
It's a very difficult kind
of composition to write.
Bach was a master of writing these.
But within that structure,
if you're able to write one,
you can make really beautiful music.
Quines give us a very rigid
structure for a program,
it has to follow these
two rules, which are not
the easiest thing to follow,
and there are other ways
to make quines besides this
way, but within that structure
we can play around and have fun.
Again, Yusuke Endoh, our favorite Rubyist,
came up with a quine,
with the help of a guy
named Darren Smith,
and it looks like this.
This is a valid Ruby program,
and if you run this
program, it returns itself.
Pretty easy.
Now what all that extra stuff
does, is it makes it so that
if you delete any character
from this program,
and I mean any character
within this program,
it still returns the original program.
In Yusuke Endoh's original implementation,
he stored the string for
the program multiple times,
compared them, found the longer one,
and then would print
out that multiple times.
Darren Smith came up with
a much better way to do it,
which is to actually have the
program know what it should
look like, and heal any mistakes in it.
Unfortunately, his version only worked
in Ruby 1.8 and before, so
Yusuke Endoh took that program,
updated it a bit so it would
work in modern versions
of Ruby, and then turned it
into this nice bit of ASCII art.
So how can you have fun with quines?
That program is really difficult to write.
I have yet to figure out all
the details of how it works,
just 'cause I haven't had
the time to read that.
(laughs softly)
But it's actually not that
hard to write your own
fun little quine.
Here's our original quine,
stripping out all that
unnecessary whitespace,
and we're gonna add a little bit of code
onto the end of it.
We're just gonna output
the result of one plus one.
Now, if we run that, it
returns two, as we'd expect.
But you'll note that the
fact that it's a quine
is still maintained.
Even though we added code on
the inside of that string,
it's still outputting
the original program,
with that addition.
And this means that we can add anything
to this particular quine,
and get it to output
whatever we want.
Now, the string that
we're evaluating as code
has access to the string that
we're evaluating as code,
as a string.
That variable s, which contains
the string, is available
within the code that we're
putting inside that string.
That means the code can modify itself.
The code can change what it does.
So I started thinking at one point.
I was working on a different
quine, and I was staying up
really late watching
the DeepMind Go games,
and because it was very late at night,
and because I was watching
a very complicated game,
and because I was working on a quine,
my brain was kind of in a weird space.
So I wondered, what if
I wrote a chess program,
which is a quine, and
the chess board is drawn
using ASCII art, where
the actual code itself
draws out the chess board?
I thought of two ways to do this.
One would be to strip
out all the whitespace
from the program before evaluating it,
because some of that whitespace
that we want to insert
to make the ASCII art may split
up Ruby methods and things,
and turn it into invalid code.
So that would look something like this.
We do our normal quine, and
we put our magic quine sauce
on the inside of that string,
and then we just strip out
all the whitespace from
that before we eval it,
and if we add that little
gsub bit inside the quine,
everything will work out just fine.
This would be easy, for
some definition of easy.
You still have to write
a valid chess program
and know how to make a quine.
But where's the fun in easy?
That brings us to option two.
We could write a program
which runs when whitespace
is inserted almost anywhere in it.
So let's walk through
what that would mean.
Here we have a very, very
fancy bit of Ruby code.
Ruby is relatively flexible in its syntax,
so if we add a newline
character before the dot,
this is still a valid Ruby
program that runs the same code.
If we put the dot on the other side
of the newline character,
we still have a valid Ruby program.
This still runs the exact same code.
We could also put a space in there,
which a lot of people aren't aware of.
This is still the exact same program,
runs the exact same code.
We could put the dot on the
other side of the space,
or my favorite, you can put
space on either side of the dot,
and this actually works in Ruby.
I don't know why anyone
would want to do this,
it's probably just an artifact
of how the parser works,
but this is still a valid Ruby code.
The problem is this:
what happens if a space
needs to be inserted
to make the ASCII art in
the middle of a method name?
Now this is still a valid Ruby code,
but it's different than
the program we had before.
Similarly, if we put a
space in the variable name,
that's gonna be a
different bit of Ruby code.
Now, we could make all of our values
single-character variables.
That would get us partway there.
And we could define all of our methods
as single-character methods,
and then have a header
in our program which defines
out all of our methods
as single-character
methods, and then we'd have
a nice bit of code which would allow us to
insert spaces anywhere.
But is there a better way to do this?
And by better, I mean a lot worse.
A lot more confusing.
'Cause that almost looks
like readable code.
Lambdas, in Ruby, can be
represented like this.
Use the arrow syntax for the lambda,
and as many people don't actually know,
you can use square
brackets to call lambdas.
This allows you to have
a lambda which kind of
acts like a function or an
array, or the most common
use I've seen for it,
is people giving talks
about lambdas, like me, who
need a very compact syntax.
Here we have a function
which takes one argument,
and at the end of it, we pass
b into that one argument,
and evaluate some code.
Lambdas, this particular
syntax, lambdas in Ruby,
is really nice, because we can put a space
or a newline character
in every third character,
and this allows us to have
three-character pixels.
Now, we do have the problem
that the last line of code,
right there, only has one character in it,
but if we put parens around the b,
we haven't added any new
characters to our syntax,
we still just have
letters, arrows, and then
all three kinds of braces, and we have it.
So now all that we need
to do is make sure that
some code that we insert
on the inside is also
only made out of lambdas,
which only contain lambdas,
which only contain lambdas,
which only contain lambdas.
How can we write a program
that has all of the rules
of chess, only using lambdas?
Alanzo Church comes to the rescue.
Alanzo Church was a
contemporary of Alan Turing,
and before Turing came up
with the Turing machine,
he came up with the lambda calculus.
He proved that, if you
have lambdas like that,
you have a Turing complete language.
That is all that you need to
implement all of programming.
There have been a number of Ruby talks,
which you can go to, which explain
how this works using Ruby.
So all we have to do is define
all of the rules of chess
in untyped lambda calculus,
and make a chess AI,
only using untyped lambda calculus.
How hard can this be?
Well, I started on it about a month ago,
and I finally got it fixed
up and good about a day ago.
(laughs)
So let's go ahead and take a look.
We'll exit out of this.
We will return to the command line,
make it a little bit bigger.
We're going to cat lambda chess.
And here we have a program.
It's a long program, but
if we look at the top,
we call eval, like we did before,
we have our variable assignment,
we're using that %q
syntax for doing strings,
that makes it a little bit more confusing,
but also gives us access to
both kinds of quotes inside,
and then we have the c
variable inside that string,
and we assign that to a lambda,
which inside of it has another
lambda, and another lambda,
and another lambda.
A lot of lambdas.
I actually ran out of
single-letter characters
in the English alphabet, so I used
all of the Greek alphabet and
most of the Russian alphabet,
and a number of other Unicode characters.
But if we go through all of
that, we eventually can have
all of the rules of chess,
and a chess AI, encoded
in untyped lambda calculus.
We then need a little bit
of helper code in Ruby
to encode what pieces
look like, to take input
from standard in and translate
them into this lambda format,
'cause remember, we don't
understand numbers or arrays,
we only understand lambdas,
and then we get lambdas back,
so we need a little bit of
Ruby code that goes through
and translates all of those
lambdas back into something
that we can output.
And then at the very end,
here, we will see a puts,
and then a big string concatenation
which puts together the final program,
replacing all the whitespace.
If we zoom out of this program,
we will see that all of that
whitespace makes it pretty.
We have lambda chess.
Now, let's see if this works.
We are going to go ahead and
run this program using Ruby.
It takes a moment, and
then we'll see we get back
the exact same program,
except all of the whitespace
has been shifted around,
so that we have a chessboard.
Seems pretty nice.
(audience applauds)
All right.
Now, to prove to you this really works,
I need a volunteer from the audience,
preferably someone named Aaron Patterson.
(laughs)
I was gonna call on
anyone, but I can't resist.
Do you know chess?
- [Aaron] Yes.
- Okay, can you give me
a valid move for white?
You're gonna be playing
against Ruby, who is black.
- [Aaron] Well, I'm
not very good at chess.
- Don't worry, this things beats me.
I'm terrible. (laughs)
Just a valid move, any move.
- [Aaron] Which one am I? The bottom?
- Yeah, bottom.
- [Aaron] One of the
pawns in row two, up--
- [Audience Member] King's
Pawn to King's Pawn two?
- [Aaron] Yeah, King's
Pawn to King's Pawn two.
- King's Pawn to King's Pawn two, okay.
All right, so this program
takes inputs from standard in,
and let's go ahead and give us a new line
so it's a little bit easier to read.
All righty, so we wanna do King's pawn,
which the King is on
the, is it the D rank?
- [Aaron] E.
- E, okay, thank you.
So it's gonna be E2,
and we are going to E4.
You don't have to put the colon in there,
the syntax is a little bit flexible,
'cause I didn't want to deal with parsing,
so I just stripped out anything
that didn't make any sense.
We're gonna run this program, now.
As you can imagine, this
takes a little bit of time.
Ruby isn't the best at optimizing this,
and lambdas aren't as fast
as they could be in Ruby,
so if anyone's looking
for a good benchmark
of lambdas in Ruby, go ahead and use this.
And we'll see that it moved the Pawn,
and came up with a valid response.
(audience applauds)
Now, this is all fine and
good, but this is a quine.
So let's go ahead and take that program,
and we'll pipe it in
using the tee operation,
into a Ruby file.
We'll go ahead and run that.
So this is just going
to output the program
that we're piping in to standard out,
and then save it off to a file.
All right, and let's just
make sure that that worked.
Now, there's a little bit of randomness
that's out of this code, so the response
isn't gonna be exactly the same.
The same piece is still moved, but okay,
the computer decided to respond that way,
We will then go ahead and
zoom back in, clear that out.
We will call ruby with that
new program that we wrote,
and we'll put in a valid
response, so let's take
the Knight at B1, and we'll move it up,
let's say, to C...
- [Audience Member] (mumbles)
- Oh, no it's...
Hmm?
- [Audience Member] C3.
- C3, thank you.
Pair programming.
(audience laughs)
All right, so now we're taking the program
that we rendered
previously, and calling it
with another move.
It's gonna go ahead and
run this for a while.
No pressure at all on me,
'cause I'm really hoping
this works. (laughs softly)
I actually know it works,
it just takes a long time.
I actually wrote a test suite for this.
(audience laughs)
And so I'll go ahead and move the Pawn,
and then it came up with
a valid response move.
All righty, which that's--
(audience members speaking)
Hmm?
Oh, it moved the pawn
up one more, thank you.
A little bit hard to notice.
Now, this program isn't just
a primitive implementation
of chess, for those of you who know chess,
you can perform castling in this program,
and the AI will do castling,
you can do promotions by
just adding an additional
character on the end,
so if you're going up
to the last row, you
will validate that that's
a valid promotion, and
it will promote the piece
to one of the valid pieces.
The opponent will also
promote, but it will always
promote to Queen, and if
you're familiar with the rules
of chess, there's an obscure
rule called en passant captures
which I don't have time to go into.
It is the bane of chess
programmers' existence.
This will do en passant
captures, which is why we have to
store the previous state of the game.
A healthy portion of the
code is just encoding that.
(audience laughs)
All righty, so there we have it.
Lambda chess.
(audience applauds)
That program I just put up on GitHub,
so if you want to play
around with it, it's there.
I also have a folder in there
which has the expanded version
of it, which has names
for all those functions,
and you can go through
that and see how it works,
though understand that
it is not the easiest
Ruby code to read, but
there's about 230 tests
to sort of guide you through it.
Though, they're not the prettiest tests
because it's not the prettiest program.
And I didn't want to
write the cleanest test
because that would take extra time.
So why would we do any of this?
The most concise explanation I have found
for explaining to my
co-workers why I spend my time
writing programs like
this, is this photo of me
when I was younger.
I just enjoy doing really
stupid, tedious tasks.
If it's a fidgety thing that
just takes lots of time,
awesome, I'll just go ahead and do that.
But also, I like programming.
I just think programming is fun.
And, you know, it's good
to write production code,
it's good to work on open-source projects,
it's good to make the world a better place
by writing good code
for other people to use,
but sometimes it's fun to just write code
for the sake of writing code.
And these exercises
are really challenging,
but they're almost guaranteed
to never be of use to anyone.
And sometimes it's nice to just have fun,
to not worry about writing
code that's interesting,
just write code that's amusing,
to amuse other programmers.
And pretty much only other programmers.
Most people will have no idea
what any of this stuff does.
If you want to learn more
about this, there are a number
of places you can go.
There's a lovely talk called
Programming with Nothing,
which was given by Tom Stuart.
This shows you how to write FizzBuzz
in untyped lambda calculus,
and he steps through it,
step by step, though he
skips over subtraction,
because addition is easy.
Subtraction is really hard.
I'll give you a hint: it's
done via repeated addition.
(laughs)
Another talk is Y Not? Adventures
in Functional Programming.
This was a keynote given at
RubyConf a number of years ago.
It is a fantastic, if
incredibly hard-to-follow, talk
in which Jim Weirich
implements the Z combinator,
which is a form of the Y combinator.
What that does is, if you
have lambdas like this,
if you're only using
lambdas, you'll note that
we didn't have variable
assignment, that meant that
a function can't call itself.
We can't do recursion.
But it's a Turing complete
language, and other
Turing complete languages
can do recursion.
The Y combinator is a magical
thing that lets you do that
through math, and Jim
Weirich atttempts, attempts
to show you how you can implement
the Y combinator yourself
using Ruby, and it's a fantastic talk.
I'd recommend also going to
at least the YouTube page
that Yusuke Endoh has.
Look at his videos, especially
I'd recommend starting
with his fluid dynamic simulation in C.
He writes the most amazingly terrible code
you have ever seen, and I
mean that as a compliment.
It is beautiful, it is
wonderful, it is mind-boggling
the things that he can do with Ruby in C.
He has written other amazing quines,
which I really recommend checking out.
He also has a GitHub page.
If you want to see how
to write a Ruby program
that returns a JavaScript
program, that returns
a REXX program, that
returns a valid C program,
though not necessarily in that order,
around 100 programming
languages, and then eventually
returns to the original Ruby program,
go to his GitHub repository.
It's incredible. (laughs)
Thank you, everyone, for coming.
If you have time, I can
take a couple questions.
If you're interested in
seeing more code like this,
follow me on Twitter.
I haven't posted anything
yet, but I'm gonna slowly,
over time, be sharing
more quines like this,
and if you wanna see what I'm up to,
and see this kind of code,
go ahead and follow me.
If you never want to see
code like this again,
or if you dislike pugs,
don't follow me on Twitter.
Also, don't talk to me. (laughs)
(audience laughs)
If you want to get in contact
with me, you can go ahead
and go on gmail, and again,
I am gonna be putting this
talk on GitHub shortly, but
all the code that I showed you
that I wrote is available on
GitHub, and it's open-source,
so if you want to play with it,
you are in your rights to do so.
Thank you.
(audience applauds)
