WEBVTT

00:00:00.000 --> 00:00:02.400 align:middle line:90%


00:00:02.400 --> 00:00:04.650 align:middle line:84%
The title of this
talk is Boundaries.

00:00:04.650 --> 00:00:07.597 align:middle line:84%
This is a story about
what I've been thinking

00:00:07.597 --> 00:00:09.180 align:middle line:84%
about for the last
year, and I'm going

00:00:09.180 --> 00:00:12.007 align:middle line:84%
to tell it including
all of my own contexts

00:00:12.007 --> 00:00:13.840 align:middle line:84%
that brought me to think
about these things.

00:00:13.840 --> 00:00:17.940 align:middle line:84%
So we're going to start off
with a little bit of background.

00:00:17.940 --> 00:00:18.810 align:middle line:90%
I'm Gary Bernhardt.

00:00:18.810 --> 00:00:21.243 align:middle line:84%
I look at this on the
internet, where I sometimes

00:00:21.243 --> 00:00:23.160 align:middle line:84%
talk about how things
should be, but usually I

00:00:23.160 --> 00:00:26.137 align:middle line:90%
get mad about how things are.

00:00:26.137 --> 00:00:27.720 align:middle line:84%
And I own a company
called Destroy All

00:00:27.720 --> 00:00:32.159 align:middle line:84%
Software, as was just mentioned,
that produces screencasts.

00:00:32.159 --> 00:00:34.110 align:middle line:84%
So starting off this
talk, the first topic

00:00:34.110 --> 00:00:35.420 align:middle line:90%
is going to be test doubles.

00:00:35.420 --> 00:00:37.170 align:middle line:84%
And this is not a talk
about test doubles,

00:00:37.170 --> 00:00:40.005 align:middle line:84%
but they're part of the
background for the ideas

00:00:40.005 --> 00:00:41.380 align:middle line:84%
that I really want
to talk about.

00:00:41.380 --> 00:00:45.480 align:middle line:84%
So let's quickly look at a
simple example of an isolated

00:00:45.480 --> 00:00:46.560 align:middle line:90%
unit test.

00:00:46.560 --> 00:00:48.330 align:middle line:84%
This is a test for a
class called sweeper.

00:00:48.330 --> 00:00:50.800 align:middle line:84%
It's part of a recurring
billing system.

00:00:50.800 --> 00:00:53.190 align:middle line:84%
And whenever a user
is actively subscribed

00:00:53.190 --> 00:00:54.855 align:middle line:84%
but hasn't paid
within the last month,

00:00:54.855 --> 00:00:56.480 align:middle line:84%
we want to sweep him
out of the system.

00:00:56.480 --> 00:00:57.870 align:middle line:84%
We want to deactivate
his account

00:00:57.870 --> 00:01:01.530 align:middle line:84%
and email him to tell him
that his card isn't working.

00:01:01.530 --> 00:01:04.900 align:middle line:84%
The context we're in is when
a subscription is expired,

00:01:04.900 --> 00:01:09.090 align:middle line:84%
so let's create a world that
is that world as described.

00:01:09.090 --> 00:01:10.800 align:middle line:84%
So we're going to
have a user, Bob.

00:01:10.800 --> 00:01:13.133 align:middle line:84%
He's going to be an active
user who paid two months ago,

00:01:13.133 --> 00:01:15.210 align:middle line:90%
so he's overdue.

00:01:15.210 --> 00:01:17.220 align:middle line:84%
We'll have an array of
users that just contains

00:01:17.220 --> 00:01:19.530 align:middle line:90%
Bob for convenience later.

00:01:19.530 --> 00:01:22.650 align:middle line:84%
And before every test, we will
stub out the user to all method

00:01:22.650 --> 00:01:24.210 align:middle line:90%
to return that array of Bob.

00:01:24.210 --> 00:01:26.460 align:middle line:84%
So we are removing the
database from the equation,

00:01:26.460 --> 00:01:30.630 align:middle line:84%
isolating ourselves from
that part of the system.

00:01:30.630 --> 00:01:32.270 align:middle line:84%
The actual example
in this case is

00:01:32.270 --> 00:01:34.020 align:middle line:84%
that we want to email
the user to tell him

00:01:34.020 --> 00:01:35.040 align:middle line:90%
that he has expired.

00:01:35.040 --> 00:01:38.610 align:middle line:84%
So we will invoke the
sweeper, and as it's running

00:01:38.610 --> 00:01:41.690 align:middle line:90%
we expect it to call UserMailer.

00:01:41.690 --> 00:01:45.052 align:middle line:84%
billing problem on that
user Bob to send that email.

00:01:45.052 --> 00:01:47.010 align:middle line:84%
But we're mocking that
out, so we're once again

00:01:47.010 --> 00:01:48.840 align:middle line:90%
isolating ourselves.

00:01:48.840 --> 00:01:51.120 align:middle line:84%
This unit test is fully
isolated because only code

00:01:51.120 --> 00:01:52.380 align:middle line:90%
in the sweeper will execute.

00:01:52.380 --> 00:01:54.720 align:middle line:84%
The user class
dependency is removed,

00:01:54.720 --> 00:01:58.500 align:middle line:84%
the mailer dependency
is removed.

00:01:58.500 --> 00:02:01.420 align:middle line:84%
The production code for
this is pretty simple.

00:02:01.420 --> 00:02:04.020 align:middle line:84%
We, first of all, pull all
the users out of the database

00:02:04.020 --> 00:02:05.670 align:middle line:84%
and select only the
ones who are active

00:02:05.670 --> 00:02:08.470 align:middle line:90%
and paid within the last month.

00:02:08.470 --> 00:02:11.220 align:middle line:84%
And then we take each of
those and we email them.

00:02:11.220 --> 00:02:13.790 align:middle line:84%
Now, doing this is not
the best way to do this.

00:02:13.790 --> 00:02:15.300 align:middle line:84%
You don't want to pull all
the users out of the database

00:02:15.300 --> 00:02:17.050 align:middle line:84%
and query them, but
I'm simplifying things

00:02:17.050 --> 00:02:19.620 align:middle line:90%
because it's a talk.

00:02:19.620 --> 00:02:22.500 align:middle line:84%
So what we have here is a test
running against the sweeper.

00:02:22.500 --> 00:02:24.360 align:middle line:84%
The sweeper wants to
touch user and mailer,

00:02:24.360 --> 00:02:26.485 align:middle line:84%
but we've replaced them
with stubs and mocks, which

00:02:26.485 --> 00:02:29.795 align:middle line:84%
means while the test is running,
the world that it sees is this.

00:02:29.795 --> 00:02:33.250 align:middle line:90%
It doesn't see the real classes.

00:02:33.250 --> 00:02:37.080 align:middle line:84%
So that's the idea of test
isolation via mocks and stubs.

00:02:37.080 --> 00:02:39.360 align:middle line:84%
There are some very good
things that come out of this,

00:02:39.360 --> 00:02:41.410 align:middle line:84%
and there's one huge,
horrible problem.

00:02:41.410 --> 00:02:44.010 align:middle line:84%
The good things, which are
actually harder to see,

00:02:44.010 --> 00:02:46.860 align:middle line:84%
are that it enables a higher
level of test-driven design

00:02:46.860 --> 00:02:49.560 align:middle line:84%
because if you're mocking
out six dependencies

00:02:49.560 --> 00:02:51.570 align:middle line:84%
and two of them are
mocked three levels deep,

00:02:51.570 --> 00:02:54.360 align:middle line:84%
three methods calls deep,
you have a design problem.

00:02:54.360 --> 00:02:57.360 align:middle line:84%
And it will be very clearly
articulated to you in your test

00:02:57.360 --> 00:03:00.150 align:middle line:84%
because you have this huge
array of mocks being created

00:03:00.150 --> 00:03:02.400 align:middle line:90%
or stubs being created.

00:03:02.400 --> 00:03:04.145 align:middle line:84%
It enables outside-in
TDD, and that

00:03:04.145 --> 00:03:06.270 align:middle line:84%
means that, for example,
we could write the sweeper

00:03:06.270 --> 00:03:09.338 align:middle line:84%
before user or the mailer
even exists because we're

00:03:09.338 --> 00:03:10.380 align:middle line:90%
mocking them out any way.

00:03:10.380 --> 00:03:11.910 align:middle line:84%
They don't need
to be there, so we

00:03:11.910 --> 00:03:14.093 align:middle line:84%
can build the highest
level of the system,

00:03:14.093 --> 00:03:15.510 align:middle line:84%
then start going
down and building

00:03:15.510 --> 00:03:18.080 align:middle line:90%
the user and the mailer.

00:03:18.080 --> 00:03:20.010 align:middle line:90%
It enables extremely fast tests.

00:03:20.010 --> 00:03:22.680 align:middle line:84%
This is part of the
whole fast rails tests

00:03:22.680 --> 00:03:25.045 align:middle line:84%
meme that's been going on
if you're in the Ruby world.

00:03:25.045 --> 00:03:26.670 align:middle line:84%
We're talking here
about the difference

00:03:26.670 --> 00:03:30.360 align:middle line:84%
between a 200-millisecond
test and a 30-second test that

00:03:30.360 --> 00:03:32.040 align:middle line:84%
has to boot your
entire web app just

00:03:32.040 --> 00:03:35.320 align:middle line:84%
to run a couple of
small unit tests.

00:03:35.320 --> 00:03:38.160 align:middle line:84%
These are all things that
are very good to have,

00:03:38.160 --> 00:03:40.500 align:middle line:84%
but they come with a
huge downside that's

00:03:40.500 --> 00:03:42.120 align:middle line:84%
much more obvious
than these benefits,

00:03:42.120 --> 00:03:44.700 align:middle line:84%
and that is that you're
running your test against mocks

00:03:44.700 --> 00:03:46.200 align:middle line:84%
and stubs, but in
production, you're

00:03:46.200 --> 00:03:47.780 align:middle line:84%
running against a
user and a mailer.

00:03:47.780 --> 00:03:49.530 align:middle line:84%
And if your mocks and
stubs don't line up,

00:03:49.530 --> 00:03:51.540 align:middle line:84%
things go out of sync,
and your tests will pass,

00:03:51.540 --> 00:03:53.850 align:middle line:84%
but your production
system will be broken.

00:03:53.850 --> 00:03:55.290 align:middle line:84%
Very obvious
problem, and this is

00:03:55.290 --> 00:03:57.480 align:middle line:84%
what people see when they
think about isolated unit

00:03:57.480 --> 00:03:58.980 align:middle line:84%
testing for the
first time, and they

00:03:58.980 --> 00:04:00.690 align:middle line:84%
think, "This is obviously
stupid," because they

00:04:00.690 --> 00:04:02.482 align:middle line:84%
haven't thought about
these subtle benefits

00:04:02.482 --> 00:04:05.490 align:middle line:90%
that it also provides.

00:04:05.490 --> 00:04:08.370 align:middle line:84%
So that is a very
brief introduction

00:04:08.370 --> 00:04:11.670 align:middle line:84%
to test doubles to
isolated unit testing.

00:04:11.670 --> 00:04:15.180 align:middle line:84%
Now, how can we
fix those problems?

00:04:15.180 --> 00:04:17.790 align:middle line:84%
Well, there have been
several mechanisms

00:04:17.790 --> 00:04:21.372 align:middle line:84%
proposed over the years,
and built in some cases.

00:04:21.372 --> 00:04:23.580 align:middle line:84%
One of them is the idea of
contract and collaboration

00:04:23.580 --> 00:04:24.080 align:middle line:90%
tests.

00:04:24.080 --> 00:04:27.120 align:middle line:84%
This is something advocated
by JB Rainsburger, who

00:04:27.120 --> 00:04:30.930 align:middle line:84%
taught me a lot about
isolated unit testing.

00:04:30.930 --> 00:04:33.840 align:middle line:84%
And this is basically the idea
of writing even more tests

00:04:33.840 --> 00:04:36.433 align:middle line:84%
to verify that all the
boundaries are correct.

00:04:36.433 --> 00:04:38.850 align:middle line:84%
And I've not actually done
this, so I can't comment on it,

00:04:38.850 --> 00:04:41.430 align:middle line:84%
but I want to mention
it for completeness.

00:04:41.430 --> 00:04:42.860 align:middle line:90%
There is the tools approach.

00:04:42.860 --> 00:04:45.360 align:middle line:84%
There's a tool in Ruby called
rspec-fire that will make sure

00:04:45.360 --> 00:04:47.700 align:middle line:84%
that anything you mock,
whenever you call a method,

00:04:47.700 --> 00:04:49.860 align:middle line:84%
that method must actually
exist on the real object.

00:04:49.860 --> 00:04:53.250 align:middle line:84%
So it sort of prevents
simple boundary mistakes.

00:04:53.250 --> 00:04:55.450 align:middle line:84%
You can use static typing
to solve this problem.

00:04:55.450 --> 00:04:59.130 align:middle line:84%
You can think of every mock as
a subclass of the real class.

00:04:59.130 --> 00:05:00.990 align:middle line:84%
It just nulls out all
the implementations,

00:05:00.990 --> 00:05:02.615 align:middle line:84%
and then you'll get
a type error if you

00:05:02.615 --> 00:05:05.220 align:middle line:84%
try to call the wrong
method on the mock.

00:05:05.220 --> 00:05:06.900 align:middle line:84%
So you have a more
testing approach,

00:05:06.900 --> 00:05:09.480 align:middle line:84%
you have a tools approach,
and you have a type system

00:05:09.480 --> 00:05:11.160 align:middle line:90%
approach.

00:05:11.160 --> 00:05:13.630 align:middle line:84%
And the fourth solution
is the most obvious one,

00:05:13.630 --> 00:05:15.390 align:middle line:84%
which is just write
the integration test.

00:05:15.390 --> 00:05:18.420 align:middle line:84%
Just give up on the
whole isolation thing.

00:05:18.420 --> 00:05:22.200 align:middle line:84%
The problem with this is that
integration tests are a scam.

00:05:22.200 --> 00:05:24.268 align:middle line:90%
They don't actually work.

00:05:24.268 --> 00:05:26.310 align:middle line:84%
There's a talk with this
title by JB Rainsburger,

00:05:26.310 --> 00:05:28.840 align:middle line:84%
the same guy who talks about
contract and collaboration

00:05:28.840 --> 00:05:29.340 align:middle line:90%
tests.

00:05:29.340 --> 00:05:33.510 align:middle line:84%
I highly recommend watching
Integration Tests are a Scam,

00:05:33.510 --> 00:05:36.330 align:middle line:84%
but let me give you a couple
of quick and dirty arguments

00:05:36.330 --> 00:05:40.950 align:middle line:84%
for why integration tests don't
work on a large enough scale.

00:05:40.950 --> 00:05:43.740 align:middle line:84%
First of all, if you have n
conditionals in your program,

00:05:43.740 --> 00:05:47.190 align:middle line:84%
n branches, n decisions,
you have 2 to the n paths,

00:05:47.190 --> 00:05:48.390 align:middle line:90%
order 2 to the n paths.

00:05:48.390 --> 00:05:51.130 align:middle line:84%
Some paths will be
mutually exclusive.

00:05:51.130 --> 00:05:53.410 align:middle line:84%
If you have 500 conditionals
in your program,

00:05:53.410 --> 00:05:55.260 align:middle line:84%
this is a number with
150 digits in it.

00:05:55.260 --> 00:05:57.570 align:middle line:84%
That's the number
of possible paths.

00:05:57.570 --> 00:05:59.670 align:middle line:90%
This is too large a number.

00:05:59.670 --> 00:06:02.520 align:middle line:84%
It's too hard to decide which of
those paths are worth testing,

00:06:02.520 --> 00:06:05.040 align:middle line:84%
so you quickly devolve
into this problem

00:06:05.040 --> 00:06:08.460 align:middle line:84%
of trying to segment this
massive space you can't even

00:06:08.460 --> 00:06:10.440 align:middle line:90%
think about.

00:06:10.440 --> 00:06:14.640 align:middle line:84%
As a more concrete argument,
the runtime of a test suite

00:06:14.640 --> 00:06:18.300 align:middle line:84%
is superlinear, and this is
because every time you add test

00:06:18.300 --> 00:06:19.618 align:middle line:90%
or code, you add the other.

00:06:19.618 --> 00:06:20.910 align:middle line:90%
They get added in pairs, right?

00:06:20.910 --> 00:06:22.493 align:middle line:84%
You always add test
and code together,

00:06:22.493 --> 00:06:24.690 align:middle line:90%
most of the time, at least.

00:06:24.690 --> 00:06:27.720 align:middle line:84%
And when you add a test,
you now have one more test

00:06:27.720 --> 00:06:30.180 align:middle line:84%
in the system, so the suite
just got linearly slower,

00:06:30.180 --> 00:06:33.510 align:middle line:84%
but the code you added slowed
down all the existing tests,

00:06:33.510 --> 00:06:35.022 align:middle line:90%
so they all got slightly slower.

00:06:35.022 --> 00:06:37.230 align:middle line:84%
So you have a linear component
and another component,

00:06:37.230 --> 00:06:39.480 align:middle line:84%
which I believe is also
linear, and it's definitely

00:06:39.480 --> 00:06:40.110 align:middle line:90%
superlinear.

00:06:40.110 --> 00:06:44.850 align:middle line:84%
And this is why your Rails app,
when it is 1,000 lines long,

00:06:44.850 --> 00:06:47.487 align:middle line:84%
all the tests are taking 20
milliseconds or something.

00:06:47.487 --> 00:06:49.320 align:middle line:84%
And then fast forward
two years, they're all

00:06:49.320 --> 00:06:52.230 align:middle line:84%
taking half a second, and your
suite is taking three hours,

00:06:52.230 --> 00:06:55.180 align:middle line:84%
and you're trying
to distribute it.

00:06:55.180 --> 00:06:57.870 align:middle line:84%
So integration testing does
not work as a primary testing

00:06:57.870 --> 00:07:00.240 align:middle line:84%
strategy for a large,
well-tested system,

00:07:00.240 --> 00:07:03.690 align:middle line:90%
as far as I can tell.

00:07:03.690 --> 00:07:08.310 align:middle line:84%
And with that background, the
introduction to isolation,

00:07:08.310 --> 00:07:11.400 align:middle line:84%
and all these solutions that I
personally don't have faith in,

00:07:11.400 --> 00:07:14.078 align:middle line:84%
none of them have
struck me as sufficient.

00:07:14.078 --> 00:07:15.870 align:middle line:84%
Now we can start getting
into what I really

00:07:15.870 --> 00:07:17.100 align:middle line:90%
want to talk about.

00:07:17.100 --> 00:07:21.250 align:middle line:84%
First topic is
values in a system.

00:07:21.250 --> 00:07:24.360 align:middle line:84%
Think about the idea of testing
the plus method on machine

00:07:24.360 --> 00:07:25.903 align:middle line:90%
integers only.

00:07:25.903 --> 00:07:27.570 align:middle line:84%
And you decide you
want to isolate this.

00:07:27.570 --> 00:07:30.060 align:middle line:84%
You don't want it to
interact with any third party

00:07:30.060 --> 00:07:31.440 align:middle line:90%
components, any other classes.

00:07:31.440 --> 00:07:32.610 align:middle line:90%
No other code.

00:07:32.610 --> 00:07:35.880 align:middle line:84%
What do you have to
do to isolate plus?

00:07:35.880 --> 00:07:38.220 align:middle line:84%
You don't have to do
anything because plus

00:07:38.220 --> 00:07:39.210 align:middle line:90%
is naturally isolated.

00:07:39.210 --> 00:07:41.430 align:middle line:84%
There's nothing to
isolate it from.

00:07:41.430 --> 00:07:42.953 align:middle line:84%
And it's not because
plus is simple

00:07:42.953 --> 00:07:44.370 align:middle line:84%
that it's naturally
isolated, it's

00:07:44.370 --> 00:07:46.110 align:middle line:84%
because it has two
properties that

00:07:46.110 --> 00:07:48.780 align:middle line:84%
make it easy to test
something in isolation

00:07:48.780 --> 00:07:51.360 align:middle line:84%
and to test it simply, and
those are that, number one,

00:07:51.360 --> 00:07:55.270 align:middle line:84%
it takes a value and
it returns a value.

00:07:55.270 --> 00:07:57.038 align:middle line:84%
It's not like you
can say 1 plus 2,

00:07:57.038 --> 00:07:58.830 align:middle line:84%
and you go and look in
some global variable

00:07:58.830 --> 00:08:00.288 align:middle line:84%
where it's stuck
the result, right?

00:08:00.288 --> 00:08:02.700 align:middle line:90%
It returns the number to you.

00:08:02.700 --> 00:08:05.970 align:middle line:84%
The second property is that
it has no dependencies at all.

00:08:05.970 --> 00:08:09.270 align:middle line:90%
It is self-contained.

00:08:09.270 --> 00:08:12.150 align:middle line:84%
So let's go back to the sweeper
example we started with,

00:08:12.150 --> 00:08:16.000 align:middle line:84%
and try to rewrite this in a way
that has those two properties.

00:08:16.000 --> 00:08:19.800 align:middle line:84%
So we can remove the mocks
and stubs but stay isolated.

00:08:19.800 --> 00:08:22.590 align:middle line:84%
First of all, Bob is a
stub, so he has to go,

00:08:22.590 --> 00:08:25.620 align:middle line:84%
and we will replace
him with a user object,

00:08:25.620 --> 00:08:27.930 align:middle line:84%
but this is not a live
object with behavior.

00:08:27.930 --> 00:08:28.848 align:middle line:90%
It's a struct.

00:08:28.848 --> 00:08:29.640 align:middle line:90%
It just holds data.

00:08:29.640 --> 00:08:30.500 align:middle line:90%
That's all it does.

00:08:30.500 --> 00:08:33.809 align:middle line:90%
It's little more than a hash.

00:08:33.809 --> 00:08:36.330 align:middle line:84%
We can't do the user.all
stub before every test,

00:08:36.330 --> 00:08:39.000 align:middle line:90%
so we'll just delete that.

00:08:39.000 --> 00:08:41.159 align:middle line:84%
And then finally, the
invocation of the sweeper

00:08:41.159 --> 00:08:43.740 align:middle line:84%
and the mock expectation--
we will collapse those

00:08:43.740 --> 00:08:46.290 align:middle line:84%
and say, if we
call into our code

00:08:46.290 --> 00:08:48.900 align:middle line:84%
and give it the array of
Bob, we should get back Bob

00:08:48.900 --> 00:08:50.170 align:middle line:90%
because Bob is expired.

00:08:50.170 --> 00:08:52.530 align:middle line:84%
So now what we have is
not a sweeper but just

00:08:52.530 --> 00:08:55.640 align:middle line:90%
an expired user's class.

00:08:55.640 --> 00:08:57.640 align:middle line:84%
The implementation
for this gets smaller.

00:08:57.640 --> 00:09:00.370 align:middle line:84%
In the old implementation, it
selected the relevant users

00:09:00.370 --> 00:09:01.090 align:middle line:90%
and emailed them.

00:09:01.090 --> 00:09:02.850 align:middle line:84%
Now it just selects
the relevant users.

00:09:02.850 --> 00:09:04.600 align:middle line:90%
It does not email them.

00:09:04.600 --> 00:09:06.760 align:middle line:84%
Now, where the second
half went is a topic

00:09:06.760 --> 00:09:09.910 align:middle line:84%
that we'll get to
in a few minutes.

00:09:09.910 --> 00:09:11.500 align:middle line:84%
What's happened
here is that I've

00:09:11.500 --> 00:09:13.900 align:middle line:84%
changed the system
so that the value is

00:09:13.900 --> 00:09:16.780 align:middle line:84%
the boundary between the
different parts of the system.

00:09:16.780 --> 00:09:19.720 align:middle line:84%
The value is the boundary now
into and out of this expired

00:09:19.720 --> 00:09:22.150 align:middle line:84%
users class, instead
of it destructively,

00:09:22.150 --> 00:09:27.475 align:middle line:84%
synchronously calling into
the mailer or the database.

00:09:27.475 --> 00:09:30.310 align:middle line:84%
Now, as a quick digression,
when I say value,

00:09:30.310 --> 00:09:32.830 align:middle line:84%
I use that term pretty
sloppily nowadays,

00:09:32.830 --> 00:09:34.040 align:middle line:90%
and I'll tell you why.

00:09:34.040 --> 00:09:36.610 align:middle line:84%
If we have a post object that
has a title and body and a slug

00:09:36.610 --> 00:09:40.270 align:middle line:84%
method computed from the
title, from the outside,

00:09:40.270 --> 00:09:43.690 align:middle line:84%
it's exactly the same as having
a post object with a title,

00:09:43.690 --> 00:09:45.310 align:middle line:84%
body, and slug,
where the slug is

00:09:45.310 --> 00:09:47.040 align:middle line:90%
computed at construction time.

00:09:47.040 --> 00:09:48.790 align:middle line:84%
From the outside, there
are three methods,

00:09:48.790 --> 00:09:50.790 align:middle line:84%
they return the same
values, and there's nothing

00:09:50.790 --> 00:09:52.250 align:middle line:90%
you can do to change them.

00:09:52.250 --> 00:09:53.872 align:middle line:90%
They're pure functions.

00:09:53.872 --> 00:09:56.080 align:middle line:84%
So I'll use the word "value"
to mean either of these,

00:09:56.080 --> 00:09:57.490 align:middle line:84%
even though there
may be methods on it.

00:09:57.490 --> 00:09:59.470 align:middle line:84%
If they're pure functions
of the value of the object,

00:09:59.470 --> 00:10:00.553 align:middle line:90%
I'll just call it a value.

00:10:00.553 --> 00:10:03.210 align:middle line:90%


00:10:03.210 --> 00:10:07.760 align:middle line:84%
So we can get natural isolation
by taking values and returning

00:10:07.760 --> 00:10:10.100 align:middle line:84%
values and by removing
all the dependencies.

00:10:10.100 --> 00:10:12.230 align:middle line:84%
And then we isolate for
free-- no mocks and stubs.

00:10:12.230 --> 00:10:13.022 align:middle line:90%
Everything is good.

00:10:13.022 --> 00:10:15.700 align:middle line:90%


00:10:15.700 --> 00:10:16.455 align:middle line:90%
So next topic.

00:10:16.455 --> 00:10:19.560 align:middle line:90%


00:10:19.560 --> 00:10:22.620 align:middle line:84%
What happens if we investigate
the three standard programming

00:10:22.620 --> 00:10:24.630 align:middle line:90%
paradigms in this light?

00:10:24.630 --> 00:10:27.240 align:middle line:84%
I'm going to push logic
programming off to the side.

00:10:27.240 --> 00:10:31.210 align:middle line:84%
I'll let Brian
Merrick do that later.

00:10:31.210 --> 00:10:33.480 align:middle line:84%
Here is an example
of procedural code.

00:10:33.480 --> 00:10:36.960 align:middle line:84%
When it's feeding time, we go
through each of the walruses,

00:10:36.960 --> 00:10:40.080 align:middle line:84%
and we shovel some
food into its stomach.

00:10:40.080 --> 00:10:43.050 align:middle line:84%
Now, you can tell this is
procedural for two reasons.

00:10:43.050 --> 00:10:45.298 align:middle line:84%
The each is telling
you that there is

00:10:45.298 --> 00:10:46.590 align:middle line:90%
something destructive going on.

00:10:46.590 --> 00:10:48.673 align:middle line:84%
If you write Ruby code and
the word each shows up,

00:10:48.673 --> 00:10:49.950 align:middle line:90%
there's something destructive.

00:10:49.950 --> 00:10:52.380 align:middle line:84%
Each is a no-op if there's
not a destructive bit

00:10:52.380 --> 00:10:54.090 align:middle line:90%
of code inside of it.

00:10:54.090 --> 00:10:56.010 align:middle line:84%
The second way you can
tell this is procedural

00:10:56.010 --> 00:10:58.050 align:middle line:84%
is that we know the
structure of the walrus,

00:10:58.050 --> 00:10:59.910 align:middle line:84%
we know it has a stomach,
but more importantly, we

00:10:59.910 --> 00:11:01.380 align:middle line:84%
know what we can
do to the stomach.

00:11:01.380 --> 00:11:03.840 align:middle line:84%
We know that the stomach can
have things shoveled into it.

00:11:03.840 --> 00:11:05.790 align:middle line:84%
We have a knowledge
of the deep structure

00:11:05.790 --> 00:11:09.260 align:middle line:90%
of the inside of the walrus.

00:11:09.260 --> 00:11:11.710 align:middle line:84%
Contrast this with
the 00 version,

00:11:11.710 --> 00:11:12.920 align:middle line:90%
which looks sort of similar.

00:11:12.920 --> 00:11:14.587 align:middle line:84%
It doesn't each, it's
still destructive,

00:11:14.587 --> 00:11:16.930 align:middle line:84%
but we tell the walrus
to eat the cheese,

00:11:16.930 --> 00:11:19.920 align:middle line:84%
and then the eat method
encapsulates the manipulation

00:11:19.920 --> 00:11:20.860 align:middle line:90%
of the stomach.

00:11:20.860 --> 00:11:23.530 align:middle line:84%
So this is the standard
distinction between plain,

00:11:23.530 --> 00:11:27.400 align:middle line:84%
old procedural, and 00, which
is also usually procedural.

00:11:27.400 --> 00:11:30.230 align:middle line:90%
We hide the internals.

00:11:30.230 --> 00:11:31.570 align:middle line:90%
So we have two paradigms.

00:11:31.570 --> 00:11:33.170 align:middle line:90%
Both of them involve mutation.

00:11:33.170 --> 00:11:35.830 align:middle line:84%
One of them combines data
and code into things we call

00:11:35.830 --> 00:11:37.210 align:middle line:90%
objects-- that's 00--

00:11:37.210 --> 00:11:38.980 align:middle line:84%
and the other one
separates data and code

00:11:38.980 --> 00:11:42.160 align:middle line:84%
into procedures and
data of various kinds.

00:11:42.160 --> 00:11:44.800 align:middle line:90%


00:11:44.800 --> 00:11:46.510 align:middle line:84%
The third paradigm
is functional,

00:11:46.510 --> 00:11:47.788 align:middle line:90%
so let's see that version.

00:11:47.788 --> 00:11:49.830 align:middle line:84%
Instead of doing an each
now, we're going to map.

00:11:49.830 --> 00:11:52.810 align:middle line:84%
So we're going to build
an array of new walruses,

00:11:52.810 --> 00:11:55.030 align:middle line:84%
and the old one's just
going to be discarded.

00:11:55.030 --> 00:11:56.530 align:middle line:84%
For each walrus,
we're going to call

00:11:56.530 --> 00:11:58.450 align:middle line:90%
eat on it with some cheese.

00:11:58.450 --> 00:12:01.030 align:middle line:84%
And the eat method, or the
eat function in this case,

00:12:01.030 --> 00:12:02.630 align:middle line:90%
is kind of confusing.

00:12:02.630 --> 00:12:04.790 align:middle line:84%
I'm going to represent
a walrus as a hash,

00:12:04.790 --> 00:12:08.020 align:middle line:84%
a stomach as an array,
and food as a string.

00:12:08.020 --> 00:12:10.720 align:middle line:84%
So we first have to
build a new stomach that

00:12:10.720 --> 00:12:12.628 align:middle line:84%
is the old stomach
plus the new food,

00:12:12.628 --> 00:12:14.170 align:middle line:84%
and then we build
a new walrus that's

00:12:14.170 --> 00:12:17.130 align:middle line:84%
the old walrus except
with the stomach replaced.

00:12:17.130 --> 00:12:22.373 align:middle line:84%
And you can sort of see why 00
is a little better at modeling

00:12:22.373 --> 00:12:24.790 align:middle line:84%
the real world in some cases
because this isn't really how

00:12:24.790 --> 00:12:26.558 align:middle line:90%
things work.

00:12:26.558 --> 00:12:28.600 align:middle line:84%
And also, it's kind of
awkward because it's Ruby,

00:12:28.600 --> 00:12:31.910 align:middle line:84%
and Ruby's not designed
to do this kind of stuff.

00:12:31.910 --> 00:12:33.850 align:middle line:84%
But in any case, we now
have three paradigms.

00:12:33.850 --> 00:12:36.710 align:middle line:84%
Functional does not
involve mutation,

00:12:36.710 --> 00:12:38.290 align:middle line:84%
but it does separate
data and code,

00:12:38.290 --> 00:12:40.210 align:middle line:84%
so it's like procedural
where nothing changes.

00:12:40.210 --> 00:12:43.840 align:middle line:84%
That's sort of what
functional programming is.

00:12:43.840 --> 00:12:46.060 align:middle line:84%
There's obviously a fourth
entry that should be here,

00:12:46.060 --> 00:12:47.935 align:middle line:84%
not just because I left
space, but because we

00:12:47.935 --> 00:12:50.380 align:middle line:84%
have two independently
varying variables,

00:12:50.380 --> 00:12:52.490 align:middle line:90%
and we only have three rows.

00:12:52.490 --> 00:12:54.910 align:middle line:84%
So let's see what the
fourth version looks like.

00:12:54.910 --> 00:12:57.143 align:middle line:84%
We still map like in
functional programming.

00:12:57.143 --> 00:12:58.810 align:middle line:84%
We're not changing
anything-- we're just

00:12:58.810 --> 00:13:00.227 align:middle line:84%
building a new
array of walruses--

00:13:00.227 --> 00:13:02.500 align:middle line:84%
but now we tell the
walruses to eat.

00:13:02.500 --> 00:13:03.880 align:middle line:90%
This is not an 00 tell.

00:13:03.880 --> 00:13:08.180 align:middle line:84%
This is not change yourself
to have eaten this.

00:13:08.180 --> 00:13:10.180 align:middle line:84%
Instead, eat makes
a new walrus that

00:13:10.180 --> 00:13:12.460 align:middle line:84%
is the old walrus with the
new food in its stomach.

00:13:12.460 --> 00:13:14.170 align:middle line:84%
So it's like the
functional code,

00:13:14.170 --> 00:13:16.860 align:middle line:84%
except it's constructing a new
object instead of constructing

00:13:16.860 --> 00:13:18.760 align:middle line:90%
a new primitive.

00:13:18.760 --> 00:13:22.540 align:middle line:84%
And I half-jokingly call this
faux0 because it looks sort

00:13:22.540 --> 00:13:25.660 align:middle line:90%
of like 00, but it's not.

00:13:25.660 --> 00:13:28.750 align:middle line:84%
There's no mutation here,
but we do combine data

00:13:28.750 --> 00:13:31.330 align:middle line:84%
and code together into things
that we could reasonably

00:13:31.330 --> 00:13:31.990 align:middle line:90%
call objects.

00:13:31.990 --> 00:13:35.470 align:middle line:90%


00:13:35.470 --> 00:13:39.460 align:middle line:84%
So now we have this
fourth, sort of, paradigm.

00:13:39.460 --> 00:13:42.130 align:middle line:84%
I wouldn't actually consider
it on the level of the three

00:13:42.130 --> 00:13:44.560 align:middle line:90%
classical paradigms.

00:13:44.560 --> 00:13:46.960 align:middle line:84%
We have a name for this
way of building software,

00:13:46.960 --> 00:13:49.135 align:middle line:84%
but the problem with this
is that you can't actually

00:13:49.135 --> 00:13:51.010 align:middle line:84%
build a real system this
way because it's not

00:13:51.010 --> 00:13:52.160 align:middle line:90%
allowed to mutate.

00:13:52.160 --> 00:13:55.180 align:middle line:84%
So it can't touch a disk,
can't touch your screen,

00:13:55.180 --> 00:13:59.130 align:middle line:84%
it can't touch your keyboard,
can't touch the network.

00:13:59.130 --> 00:14:01.490 align:middle line:84%
So let's reintroduce
imperative programming

00:14:01.490 --> 00:14:05.360 align:middle line:84%
to this sort of faux0,
pseudo-functional programming

00:14:05.360 --> 00:14:09.080 align:middle line:84%
style, but do it with
intention instead

00:14:09.080 --> 00:14:12.350 align:middle line:84%
of just slapping procedural
bits into our functional code.

00:14:12.350 --> 00:14:15.822 align:middle line:90%


00:14:15.822 --> 00:14:17.280 align:middle line:84%
Going back to the
original example,

00:14:17.280 --> 00:14:19.655 align:middle line:84%
we have a user database, we
have this expired users class

00:14:19.655 --> 00:14:21.860 align:middle line:84%
that can filter users,
we have the mailer,

00:14:21.860 --> 00:14:23.810 align:middle line:84%
and we need to
compose them somehow.

00:14:23.810 --> 00:14:25.100 align:middle line:84%
Currently, they don't
know about each other

00:14:25.100 --> 00:14:26.392 align:middle line:90%
because I broke the boundaries.

00:14:26.392 --> 00:14:29.650 align:middle line:84%
I replaced the call boundaries
with the value boundaries,

00:14:29.650 --> 00:14:32.888 align:middle line:84%
so there's no
linkage between them.

00:14:32.888 --> 00:14:34.430 align:middle line:84%
So let's take our
expired users class

00:14:34.430 --> 00:14:37.040 align:middle line:84%
and just reintroduce the rest
of the method we took away

00:14:37.040 --> 00:14:39.290 align:middle line:84%
in the first place by
recreating the sweeper,

00:14:39.290 --> 00:14:42.815 align:middle line:84%
but now, it calls user.all
to get all the records out

00:14:42.815 --> 00:14:45.890 align:middle line:84%
of the database, sends them into
expired users to filter them,

00:14:45.890 --> 00:14:48.260 align:middle line:84%
and then sends an email to
each of those users that

00:14:48.260 --> 00:14:51.320 align:middle line:90%
was expired.

00:14:51.320 --> 00:14:55.070 align:middle line:84%
Now, at this point, I expect you
to think that I've not actually

00:14:55.070 --> 00:14:56.290 align:middle line:90%
done anything.

00:14:56.290 --> 00:14:58.790 align:middle line:84%
All I've really done is split
a method in half and put names

00:14:58.790 --> 00:14:59.630 align:middle line:90%
on it.

00:14:59.630 --> 00:15:03.350 align:middle line:84%
But there's actually a couple
of very fundamental distinctions

00:15:03.350 --> 00:15:06.830 align:middle line:84%
between these two
pieces of code.

00:15:06.830 --> 00:15:10.430 align:middle line:84%
The expired users class
contains all the decisions

00:15:10.430 --> 00:15:11.510 align:middle line:90%
that are being made.

00:15:11.510 --> 00:15:15.380 align:middle line:84%
It decides whether a user is
active, whether a user is late.

00:15:15.380 --> 00:15:17.030 align:middle line:90%
It makes all the decisions.

00:15:17.030 --> 00:15:19.070 align:middle line:84%
The sweeper contains
all the dependencies.

00:15:19.070 --> 00:15:22.190 align:middle line:84%
It depends on the expired
users class, on the user class,

00:15:22.190 --> 00:15:24.800 align:middle line:90%
and on the mailer.

00:15:24.800 --> 00:15:27.050 align:middle line:84%
If we bring this
back to testing,

00:15:27.050 --> 00:15:28.640 align:middle line:84%
the functional
core, which contains

00:15:28.640 --> 00:15:30.890 align:middle line:84%
things like the
expired users class,

00:15:30.890 --> 00:15:34.940 align:middle line:84%
has lots of paths but few or
hopefully no dependencies,

00:15:34.940 --> 00:15:37.490 align:middle line:84%
which is exactly what isolated
unit testing is good at.

00:15:37.490 --> 00:15:39.590 align:middle line:90%
It's naturally isolated code.

00:15:39.590 --> 00:15:41.240 align:middle line:84%
There are a lot of
cases, but they're

00:15:41.240 --> 00:15:43.220 align:middle line:84%
easy to test because
you're naturally isolated

00:15:43.220 --> 00:15:46.460 align:middle line:84%
and you're functional,
so there's simple tests.

00:15:46.460 --> 00:15:47.960 align:middle line:84%
The imperative
shell that contains

00:15:47.960 --> 00:15:51.200 align:middle line:84%
things like the sweeper has lots
of dependencies but very few

00:15:51.200 --> 00:15:54.180 align:middle line:84%
paths, which is exactly what
integration testing is good at.

00:15:54.180 --> 00:15:56.180 align:middle line:84%
It's good at making sure
that all the pieces are

00:15:56.180 --> 00:15:57.555 align:middle line:84%
communicating
correctly, but it's

00:15:57.555 --> 00:16:00.020 align:middle line:84%
bad at testing lots
of complex cases

00:16:00.020 --> 00:16:01.850 align:middle line:84%
because then you end
up with lots of tests

00:16:01.850 --> 00:16:02.900 align:middle line:90%
that are each very slow.

00:16:02.900 --> 00:16:06.330 align:middle line:90%


00:16:06.330 --> 00:16:09.110 align:middle line:84%
So I imagine a system built
in this way looking something

00:16:09.110 --> 00:16:11.780 align:middle line:84%
like this, having these little
functional pieces in the middle

00:16:11.780 --> 00:16:14.853 align:middle line:84%
surrounded by this, sort
of, loop of imperative code

00:16:14.853 --> 00:16:16.770 align:middle line:84%
that isolates them from
the rest of the world,

00:16:16.770 --> 00:16:19.328 align:middle line:84%
including networks,
disks, and state

00:16:19.328 --> 00:16:20.870 align:middle line:84%
that they need to
maintain over time.

00:16:20.870 --> 00:16:24.610 align:middle line:90%


00:16:24.610 --> 00:16:28.320 align:middle line:84%
So now we have this way of
programming that is sort of 00,

00:16:28.320 --> 00:16:31.560 align:middle line:84%
but is computationally
as functional,

00:16:31.560 --> 00:16:33.930 align:middle line:84%
and we have a way to
marry it back to the world

00:16:33.930 --> 00:16:36.960 align:middle line:84%
of imperative, terrible things
like disks and networks that

00:16:36.960 --> 00:16:39.780 align:middle line:90%
fail and are slow.

00:16:39.780 --> 00:16:44.100 align:middle line:84%
And now I want to show
you just a random example

00:16:44.100 --> 00:16:47.230 align:middle line:84%
of how very nice things happen
when you start doing this.

00:16:47.230 --> 00:16:50.550 align:middle line:84%
So let's talk briefly
about concurrency.

00:16:50.550 --> 00:16:54.190 align:middle line:84%
Of the growing number of
concurrency models that exist,

00:16:54.190 --> 00:16:57.480 align:middle line:84%
the one I have the most faith
in as something approaching

00:16:57.480 --> 00:17:00.942 align:middle line:84%
general purpose is
the actor model.

00:17:00.942 --> 00:17:02.400 align:middle line:84%
And so I'll quickly
give you a demo

00:17:02.400 --> 00:17:03.692 align:middle line:90%
of that if you're not familiar.

00:17:03.692 --> 00:17:06.900 align:middle line:84%
I'm going to do it in Ruby
just using queues and threads.

00:17:06.900 --> 00:17:10.260 align:middle line:84%
This queue is going to be
the inbox of process 2.

00:17:10.260 --> 00:17:13.380 align:middle line:84%
Process 1 is going to
send messages to it.

00:17:13.380 --> 00:17:15.030 align:middle line:84%
For process 1, I'm
going to fork off

00:17:15.030 --> 00:17:16.738 align:middle line:84%
a thread that goes
into an infinite loop,

00:17:16.738 --> 00:17:19.393 align:middle line:84%
reading from standard in
and pushing into the queue.

00:17:19.393 --> 00:17:21.810 align:middle line:84%
For process 2, I'm going to
fork off an infinite loop that

00:17:21.810 --> 00:17:24.060 align:middle line:84%
reads from the queue and
writes to standard out, so we

00:17:24.060 --> 00:17:26.560 align:middle line:90%
have an echo program here.

00:17:26.560 --> 00:17:29.790 align:middle line:84%
If I run this at the
terminal, I can just

00:17:29.790 --> 00:17:32.280 align:middle line:84%
paste in some Ruby
code in a string.

00:17:32.280 --> 00:17:34.300 align:middle line:84%
Now it is currently
blocking on standard in,

00:17:34.300 --> 00:17:37.770 align:middle line:84%
and whatever I type into it
is going to echo back to me.

00:17:37.770 --> 00:17:39.390 align:middle line:90%
So we have two processes.

00:17:39.390 --> 00:17:42.060 align:middle line:84%
They have inboxes-- well,
only one of them has an inbox

00:17:42.060 --> 00:17:43.050 align:middle line:90%
in this case--

00:17:43.050 --> 00:17:47.400 align:middle line:84%
and they communicate by pushing
values into each other's inbox.

00:17:47.400 --> 00:17:50.370 align:middle line:84%
The way this relates
to this idea of values

00:17:50.370 --> 00:17:53.820 align:middle line:84%
or faux0 or functional
core imperative shell

00:17:53.820 --> 00:17:55.740 align:middle line:84%
is that every value
in your system

00:17:55.740 --> 00:17:58.980 align:middle line:84%
is a possible message
between the processes.

00:17:58.980 --> 00:18:02.760 align:middle line:84%
All an Erlang process does
or all any actor-based system

00:18:02.760 --> 00:18:05.640 align:middle line:84%
does is send messages
into other inboxes,

00:18:05.640 --> 00:18:08.670 align:middle line:84%
so the more potential
messages you have to send,

00:18:08.670 --> 00:18:11.975 align:middle line:84%
the more natural this
form of concurrency is.

00:18:11.975 --> 00:18:14.100 align:middle line:84%
And this is really just a
special case of the value

00:18:14.100 --> 00:18:15.150 align:middle line:90%
is the boundary.

00:18:15.150 --> 00:18:17.497 align:middle line:84%
If a value is a boundary
between two methods,

00:18:17.497 --> 00:18:19.330 align:middle line:84%
then it's also the
boundary between classes,

00:18:19.330 --> 00:18:21.240 align:middle line:84%
between subsystems,
between processes.

00:18:21.240 --> 00:18:24.780 align:middle line:90%


00:18:24.780 --> 00:18:26.280 align:middle line:84%
Let's quickly go
through the sweeper

00:18:26.280 --> 00:18:27.988 align:middle line:84%
again and convert it
into this style just

00:18:27.988 --> 00:18:30.660 align:middle line:84%
to see what the
implications are.

00:18:30.660 --> 00:18:33.810 align:middle line:84%
I've changed it slightly to make
the example go more smoothly.

00:18:33.810 --> 00:18:36.780 align:middle line:84%
We have a sweep method that
does expired users on user.all

00:18:36.780 --> 00:18:39.730 align:middle line:84%
and then mails them all--
basically the same as before.

00:18:39.730 --> 00:18:41.580 align:middle line:84%
We have the expired
users method that

00:18:41.580 --> 00:18:45.952 align:middle line:84%
filters out only the users
who are expired, like before.

00:18:45.952 --> 00:18:47.910 align:middle line:84%
And we have a trivial
notify_of_billing problem

00:18:47.910 --> 00:18:52.140 align:middle line:84%
method that just
delegates to the mailer.

00:18:52.140 --> 00:18:54.748 align:middle line:84%
Starting at the top
with the sweep method,

00:18:54.748 --> 00:18:56.040 align:middle line:90%
let's convert this to an actor.

00:18:56.040 --> 00:18:58.710 align:middle line:84%
And as an actor, it's going
to pull everything out

00:18:58.710 --> 00:19:01.860 align:middle line:84%
of the database and then push
them all into expired users

00:19:01.860 --> 00:19:02.620 align:middle line:90%
one by one.

00:19:02.620 --> 00:19:04.380 align:middle line:84%
So every user in the
database is going

00:19:04.380 --> 00:19:07.110 align:middle line:84%
to get pushed into
expired users inbox.

00:19:07.110 --> 00:19:09.610 align:middle line:84%
Once again, probably not the
ideal way to build a system,

00:19:09.610 --> 00:19:11.940 align:middle line:90%
but it's a talk.

00:19:11.940 --> 00:19:13.620 align:middle line:84%
After doing that,
it immediately dies.

00:19:13.620 --> 00:19:16.452 align:middle line:84%
Otherwise, it would
loop infinitely.

00:19:16.452 --> 00:19:17.910 align:middle line:84%
The expired users
function is going

00:19:17.910 --> 00:19:21.060 align:middle line:84%
to turn into an actor that
continuously pops a user off

00:19:21.060 --> 00:19:24.840 align:middle line:84%
of its inbox, decides whether
the user is late in line 2,

00:19:24.840 --> 00:19:29.070 align:middle line:84%
and then sends it to the
mailer only if it was late.

00:19:29.070 --> 00:19:31.248 align:middle line:84%
And finally, the mailer
is very easy to change.

00:19:31.248 --> 00:19:32.790 align:middle line:84%
It turns into an
actor that just pops

00:19:32.790 --> 00:19:36.060 align:middle line:84%
a user off, and immediately
sends the email.

00:19:36.060 --> 00:19:39.450 align:middle line:84%
So what we've done here is
convert an inherently serial

00:19:39.450 --> 00:19:42.960 align:middle line:84%
system, a single, threaded
system into a system that

00:19:42.960 --> 00:19:44.682 align:middle line:84%
will happily
pin three cores,

00:19:44.682 --> 00:19:46.140 align:middle line:84%
at least for a
brief amount of time

00:19:46.140 --> 00:19:48.440 align:middle line:84%
while it's pulling things
out of the database.

00:19:48.440 --> 00:19:51.180 align:middle line:90%
It is naturally parallel.

00:19:51.180 --> 00:19:54.240 align:middle line:84%
And it was easy to do
that, or easier to do that,

00:19:54.240 --> 00:19:57.000 align:middle line:84%
because we have these
values, like the user value,

00:19:57.000 --> 00:19:58.200 align:middle line:90%
to push around easily.

00:19:58.200 --> 00:20:00.940 align:middle line:90%


00:20:00.940 --> 00:20:03.490 align:middle line:84%
Values in your system
afford shifting processes,

00:20:03.490 --> 00:20:06.820 align:middle line:84%
but really, this
is a special case

00:20:06.820 --> 00:20:10.690 align:middle line:84%
of values in your system
affording shifting boundaries

00:20:10.690 --> 00:20:14.020 align:middle line:84%
of any kind, whether they're
between classes or subsystems

00:20:14.020 --> 00:20:14.980 align:middle line:90%
or processes.

00:20:14.980 --> 00:20:17.910 align:middle line:90%


00:20:17.910 --> 00:20:20.500 align:middle line:84%
In this example, we
only have user values

00:20:20.500 --> 00:20:24.640 align:middle line:84%
going between processes,
but there are Erlang systems

00:20:24.640 --> 00:20:26.800 align:middle line:84%
with millions of
processes running,

00:20:26.800 --> 00:20:29.080 align:middle line:84%
and they have lots and
lots of different values

00:20:29.080 --> 00:20:32.420 align:middle line:84%
to send between the
different processes.

00:20:32.420 --> 00:20:36.430 align:middle line:90%
But it's a talk, so it's simple.

00:20:36.430 --> 00:20:39.928 align:middle line:84%
So that is an example of the
relationship between this style

00:20:39.928 --> 00:20:42.220 align:middle line:84%
of programming and concurrency
and the nice things that

00:20:42.220 --> 00:20:42.730 align:middle line:90%
fall out.

00:20:42.730 --> 00:20:45.720 align:middle line:90%


00:20:45.720 --> 00:20:48.530 align:middle line:84%
Now, I did this talk
a week ago, and based

00:20:48.530 --> 00:20:51.200 align:middle line:84%
on the questions
I got afterwards,

00:20:51.200 --> 00:20:56.690 align:middle line:84%
I've added an extended example
because people didn't quite

00:20:56.690 --> 00:20:58.380 align:middle line:84%
see how the whole
system fit together.

00:20:58.380 --> 00:20:59.963 align:middle line:84%
So I'm going to show
you a real system

00:20:59.963 --> 00:21:01.760 align:middle line:90%
that I built in this way.

00:21:01.760 --> 00:21:05.450 align:middle line:84%
It is a Twitter client,
and if I fire it up,

00:21:05.450 --> 00:21:06.920 align:middle line:84%
it is an interactive
application.

00:21:06.920 --> 00:21:10.088 align:middle line:84%
So it's not just a Twitter
client that you run

00:21:10.088 --> 00:21:12.630 align:middle line:84%
and it tweets and then it exits,
it's an interactive program,

00:21:12.630 --> 00:21:14.960 align:middle line:84%
and I can scroll down
through my tweets.

00:21:14.960 --> 00:21:16.560 align:middle line:90%
This is my actual Twitter feed.

00:21:16.560 --> 00:21:18.060 align:middle line:84%
I didn't actually
read any of these,

00:21:18.060 --> 00:21:20.990 align:middle line:84%
so hopefully there's
nothing terrible up there.

00:21:20.990 --> 00:21:22.580 align:middle line:84%
There might be,
given who I follow.

00:21:22.580 --> 00:21:24.910 align:middle line:90%


00:21:24.910 --> 00:21:27.410 align:middle line:84%
So this whole Twitter client
is written in exactly this way.

00:21:27.410 --> 00:21:29.342 align:middle line:90%
I have a functional core.

00:21:29.342 --> 00:21:31.550 align:middle line:84%
The guts of the program are
all functional in nature,

00:21:31.550 --> 00:21:36.440 align:middle line:84%
and they're surrounded by
this layer of imperative code.

00:21:36.440 --> 00:21:38.230 align:middle line:84%
For the parts we're
going to talk about,

00:21:38.230 --> 00:21:39.980 align:middle line:84%
there are two external
resources that we

00:21:39.980 --> 00:21:42.200 align:middle line:84%
need to concern ourselves with,
the keyboard and the screen.

00:21:42.200 --> 00:21:43.880 align:middle line:84%
The keyboard,
fortunately, is read-only.

00:21:43.880 --> 00:21:45.380 align:middle line:84%
The screen is
write-only, so we have

00:21:45.380 --> 00:21:48.530 align:middle line:84%
a nice, unidirectional
data flow.

00:21:48.530 --> 00:21:52.520 align:middle line:84%
And part of the imperative shell
in this program, or most of it,

00:21:52.520 --> 00:21:54.740 align:middle line:90%
are things I call views.

00:21:54.740 --> 00:21:57.860 align:middle line:84%
They're really more
like iOS views,

00:21:57.860 --> 00:22:02.060 align:middle line:84%
probably not
classical MBC views.

00:22:02.060 --> 00:22:04.400 align:middle line:84%
Here's one of them,
the timeline view.

00:22:04.400 --> 00:22:07.457 align:middle line:84%
It accepts key presses
from the keyboard.

00:22:07.457 --> 00:22:09.290 align:middle line:84%
They get forwarded to
it through a mechanism

00:22:09.290 --> 00:22:11.300 align:middle line:90%
that's not very interesting.

00:22:11.300 --> 00:22:13.250 align:middle line:84%
And it decides what to
do based on the key.

00:22:13.250 --> 00:22:15.350 align:middle line:84%
If you hit C, you
compose a tweet.

00:22:15.350 --> 00:22:17.780 align:middle line:84%
That's not relevant to what
we're going to talk about,

00:22:17.780 --> 00:22:22.310 align:middle line:84%
but what is relevant is J
and K, which go down and up.

00:22:22.310 --> 00:22:27.440 align:middle line:84%
now you can see the imperative
shell's nature right here.

00:22:27.440 --> 00:22:30.710 align:middle line:84%
I have a cursor object-- the
cursor object is functional

00:22:30.710 --> 00:22:32.420 align:middle line:90%
in nature--

00:22:32.420 --> 00:22:35.390 align:middle line:84%
and I track an instance of
it in an instance variable

00:22:35.390 --> 00:22:36.800 align:middle line:90%
outside in the imperative shell.

00:22:36.800 --> 00:22:39.650 align:middle line:84%
And then when you hit J or
K, I call into the core,

00:22:39.650 --> 00:22:41.450 align:middle line:84%
the core generates a
new value, and then

00:22:41.450 --> 00:22:44.880 align:middle line:84%
I reassign it to that
instance variable.

00:22:44.880 --> 00:22:47.690 align:middle line:84%
So the core has
the intelligence,

00:22:47.690 --> 00:22:50.540 align:middle line:90%
but the shell has the state.

00:22:50.540 --> 00:22:54.140 align:middle line:84%
The state of the shell
changes over time.

00:22:54.140 --> 00:22:56.017 align:middle line:84%
After this, we get
some other keystrokes

00:22:56.017 --> 00:22:57.600 align:middle line:84%
that actually aren't
very interesting,

00:22:57.600 --> 00:22:59.517 align:middle line:84%
like jumping up and down
by a lot or quitting.

00:22:59.517 --> 00:23:02.280 align:middle line:90%


00:23:02.280 --> 00:23:05.610 align:middle line:84%
So that's the view layer, and
now let's look at the cursor

00:23:05.610 --> 00:23:09.390 align:middle line:84%
that it was using, where
all the actual work is done.

00:23:09.390 --> 00:23:11.970 align:middle line:84%
The cursor class has quite a
few methods on it, including

00:23:11.970 --> 00:23:14.910 align:middle line:90%
down, which we saw being used.

00:23:14.910 --> 00:23:17.280 align:middle line:84%
The down method, first of
all, guards against the case

00:23:17.280 --> 00:23:18.510 align:middle line:90%
when it is empty.

00:23:18.510 --> 00:23:20.100 align:middle line:84%
The cursor contains
both an array

00:23:20.100 --> 00:23:22.963 align:middle line:84%
of tweets and the
selected tweet.

00:23:22.963 --> 00:23:24.630 align:middle line:84%
And when we say down,
what we want to do

00:23:24.630 --> 00:23:28.110 align:middle line:84%
is move the selected tweet to be
the next one down in the list,

00:23:28.110 --> 00:23:29.890 align:middle line:84%
but the list of
tweets stays the same.

00:23:29.890 --> 00:23:32.140 align:middle line:84%
So if the list is empty, we
don't have to do anything,

00:23:32.140 --> 00:23:35.430 align:middle line:90%
we just return the same cursor.

00:23:35.430 --> 00:23:37.930 align:middle line:84%
We want to prevent going
off the end of the list,

00:23:37.930 --> 00:23:41.460 align:middle line:84%
so we're going to go to
either the requested tweet

00:23:41.460 --> 00:23:44.280 align:middle line:84%
or the last one, whichever
one is a lower number.

00:23:44.280 --> 00:23:47.070 align:middle line:84%
And those numbers are
computed in boring ways.

00:23:47.070 --> 00:23:50.460 align:middle line:84%
The last index is going
to be the count minus 1--

00:23:50.460 --> 00:23:51.630 align:middle line:90%
standard stuff.

00:23:51.630 --> 00:23:54.600 align:middle line:84%
The one that we want to go to
is wherever we are plus however

00:23:54.600 --> 00:23:57.090 align:middle line:90%
much we're trying to jump by.

00:23:57.090 --> 00:24:00.540 align:middle line:84%
And then finally,
the important bit--

00:24:00.540 --> 00:24:03.900 align:middle line:84%
the work we do is
return a new cursor

00:24:03.900 --> 00:24:08.490 align:middle line:84%
with the same old tweets
but a new selection.

00:24:08.490 --> 00:24:12.390 align:middle line:84%
And this is what all the code in
the functional core looks like.

00:24:12.390 --> 00:24:15.018 align:middle line:84%
It's always doing some work,
it's thinking for a while,

00:24:15.018 --> 00:24:16.560 align:middle line:84%
and then it's just
returning a value.

00:24:16.560 --> 00:24:17.685 align:middle line:90%
It doesn't change anything.

00:24:17.685 --> 00:24:20.800 align:middle line:90%


00:24:20.800 --> 00:24:24.280 align:middle line:84%
Testing code like this
turns out to be very easy.

00:24:24.280 --> 00:24:29.020 align:middle line:84%
If we look at the tests for this
cursor method, the down method,

00:24:29.020 --> 00:24:31.120 align:middle line:84%
I make two tweets,
I make a cursor

00:24:31.120 --> 00:24:33.640 align:middle line:84%
that contains the
tweets, and then

00:24:33.640 --> 00:24:34.900 align:middle line:90%
I just start writing examples.

00:24:34.900 --> 00:24:36.610 align:middle line:90%
It starts at the top.

00:24:36.610 --> 00:24:39.940 align:middle line:84%
The new cursor should have
the first tweet selected.

00:24:39.940 --> 00:24:41.140 align:middle line:90%
It moves the cursor down.

00:24:41.140 --> 00:24:43.750 align:middle line:84%
When I say cursor.down,
then the selection

00:24:43.750 --> 00:24:47.050 align:middle line:84%
should be tweet2, the
second tweet in the list.

00:24:47.050 --> 00:24:49.490 align:middle line:84%
It doesn't move below
the end of the list.

00:24:49.490 --> 00:24:53.905 align:middle line:84%
If I go down twice, then I
should still be on tweet2.

00:24:53.905 --> 00:24:56.530 align:middle line:84%
Now, you might look at this and
see a Law of Demeter violation,

00:24:56.530 --> 00:25:00.892 align:middle line:84%
but that's because 00
has poisoned your mind.

00:25:00.892 --> 00:25:02.770 align:middle line:84%
If this were a
stateful object, and I

00:25:02.770 --> 00:25:05.710 align:middle line:84%
had said cursor.down new
line, cursor.down, new line,

00:25:05.710 --> 00:25:07.650 align:middle line:84%
cursor.selection.should
equal, you

00:25:07.650 --> 00:25:09.190 align:middle line:84%
would have been
fine with it, right?

00:25:09.190 --> 00:25:10.750 align:middle line:84%
That wouldn't be a
Demeter violation.

00:25:10.750 --> 00:25:12.490 align:middle line:90%
It's exactly the same code.

00:25:12.490 --> 00:25:14.358 align:middle line:84%
The state transitions
are exactly the same,

00:25:14.358 --> 00:25:16.150 align:middle line:84%
it's just that this
allows me to express it

00:25:16.150 --> 00:25:19.480 align:middle line:90%
in a more direct way.

00:25:19.480 --> 00:25:22.990 align:middle line:84%
And it makes the test
shorter, which is always nice.

00:25:22.990 --> 00:25:24.720 align:middle line:84%
So testing this
stuff is very easy.

00:25:24.720 --> 00:25:26.740 align:middle line:84%
The whole functional core
is tested in this way,

00:25:26.740 --> 00:25:29.800 align:middle line:84%
and it was all
remarkably painless,

00:25:29.800 --> 00:25:34.145 align:middle line:84%
except when it was
algorithmically difficult.

00:25:34.145 --> 00:25:35.020 align:middle line:90%
So that's the cursor.

00:25:35.020 --> 00:25:37.240 align:middle line:84%
The view maintains the
state of the program,

00:25:37.240 --> 00:25:42.500 align:middle line:84%
it delegates into the cursor
object to compute new values.

00:25:42.500 --> 00:25:44.070 align:middle line:84%
And now let's look
at one more piece,

00:25:44.070 --> 00:25:46.540 align:middle line:84%
the way that the screen
is actually rendered.

00:25:46.540 --> 00:25:50.290 align:middle line:84%
Rendering is classically a very
stateful operation, especially

00:25:50.290 --> 00:25:52.990 align:middle line:90%
with something like OpenGL, say.

00:25:52.990 --> 00:25:55.770 align:middle line:84%
Now, I'm not running OpenGL
in the terminal, fortunately,

00:25:55.770 --> 00:25:59.050 align:middle line:84%
so I don't have
that problem, but I

00:25:59.050 --> 00:26:00.640 align:middle line:84%
do need to figure
out how to segregate

00:26:00.640 --> 00:26:03.970 align:middle line:84%
the knowledge about rendering
from the actual dumping

00:26:03.970 --> 00:26:06.700 align:middle line:90%
of bytes onto the screen.

00:26:06.700 --> 00:26:09.940 align:middle line:84%
So going back to the view
layer, the imperative layer,

00:26:09.940 --> 00:26:12.790 align:middle line:84%
I have a draw_tweets
method, and it

00:26:12.790 --> 00:26:14.923 align:middle line:84%
starts by pulling the
tweets out of the cursor.

00:26:14.923 --> 00:26:16.840 align:middle line:84%
This is basically throwing
away all the tweets

00:26:16.840 --> 00:26:18.310 align:middle line:84%
before the currently
selected one

00:26:18.310 --> 00:26:21.130 align:middle line:84%
because I don't need
them for rendering.

00:26:21.130 --> 00:26:23.140 align:middle line:84%
I guard against there
being no tweets,

00:26:23.140 --> 00:26:27.790 align:middle line:84%
and as long as there are some,
I delegate to a tweet renderer

00:26:27.790 --> 00:26:29.800 align:middle line:84%
object, and it looks kind
of gross on the screen

00:26:29.800 --> 00:26:32.590 align:middle line:84%
because I had to squish it
so you guys could read it,

00:26:32.590 --> 00:26:35.440 align:middle line:84%
but it sends in various
values to the tweet renderer.

00:26:35.440 --> 00:26:38.080 align:middle line:84%
And the tweet renderer is going
to return an array of lines

00:26:38.080 --> 00:26:40.750 align:middle line:84%
that are just the text that
should go on the screen,

00:26:40.750 --> 00:26:43.900 align:middle line:84%
and then I send that text off to
the actual screen object, which

00:26:43.900 --> 00:26:46.720 align:middle line:90%
is a destructive operation.

00:26:46.720 --> 00:26:50.440 align:middle line:84%
So you can see the way that this
piece of the imperative shell

00:26:50.440 --> 00:26:52.420 align:middle line:84%
is sort of orchestrating
the interaction

00:26:52.420 --> 00:26:55.630 align:middle line:84%
between the functional core
and the stateful, nasty world

00:26:55.630 --> 00:26:56.740 align:middle line:90%
of the screen.

00:26:56.740 --> 00:27:00.220 align:middle line:84%
The tweet renderer is a
functional piece of code.

00:27:00.220 --> 00:27:04.330 align:middle line:90%
The screen is highly stateful.

00:27:04.330 --> 00:27:06.250 align:middle line:84%
Going down into the
actual tweet renderer,

00:27:06.250 --> 00:27:07.810 align:middle line:84%
it has quite a
bit of code in it.

00:27:07.810 --> 00:27:09.970 align:middle line:84%
It's actually the most
complex part of the system,

00:27:09.970 --> 00:27:11.788 align:middle line:84%
because it has to handle
things like tweets

00:27:11.788 --> 00:27:14.080 align:middle line:84%
with new lines in them, but
also it has to wrap things,

00:27:14.080 --> 00:27:17.332 align:middle line:84%
it has to colorize things,
highlight the selected tweet.

00:27:17.332 --> 00:27:18.790 align:middle line:84%
But way down in
the guts of it, you

00:27:18.790 --> 00:27:21.020 align:middle line:84%
get this method called
lines that's actually

00:27:21.020 --> 00:27:23.410 align:middle line:84%
a tweet renderer, not
tweet list renderer,

00:27:23.410 --> 00:27:25.840 align:middle line:84%
and it is just trying to
compute the array of lines

00:27:25.840 --> 00:27:26.770 align:middle line:90%
for a single tweet.

00:27:26.770 --> 00:27:31.410 align:middle line:84%
So to do that, it iterates
over the individual lines

00:27:31.410 --> 00:27:35.320 align:middle line:84%
in the tweet, and it word wraps
each of them independently

00:27:35.320 --> 00:27:37.500 align:middle line:90%
using a wrap class.

00:27:37.500 --> 00:27:41.890 align:middle line:84%
And then for each wrapped
line, it colorizes that line,

00:27:41.890 --> 00:27:43.615 align:middle line:84%
and then it just
flattens all that

00:27:43.615 --> 00:27:47.240 align:middle line:90%
into one big array of lines.

00:27:47.240 --> 00:27:49.817 align:middle line:84%
So really boring details
down at the bottom,

00:27:49.817 --> 00:27:52.150 align:middle line:84%
but you can see that once
again, it's purely functional.

00:27:52.150 --> 00:27:54.130 align:middle line:84%
It's just generating
an array of lines

00:27:54.130 --> 00:27:57.220 align:middle line:84%
to go on screen, which is the
most boring, easy-to-test thing

00:27:57.220 --> 00:27:59.990 align:middle line:90%
in the world.

00:27:59.990 --> 00:28:04.790 align:middle line:84%
Speaking of being easy to
test, let's make a user, Gary--

00:28:04.790 --> 00:28:06.650 align:middle line:90%
I don't know why I chose that--

00:28:06.650 --> 00:28:09.890 align:middle line:90%
and two tweets written by Gary.

00:28:09.890 --> 00:28:12.230 align:middle line:84%
And then just call into
the tweet list renderer

00:28:12.230 --> 00:28:15.320 align:middle line:84%
to get the lines that should
go on the screen for those two

00:28:15.320 --> 00:28:16.515 align:middle line:90%
tweets.

00:28:16.515 --> 00:28:18.140 align:middle line:84%
And all we have to
do is assert that we

00:28:18.140 --> 00:28:19.310 align:middle line:90%
got the right thing out.

00:28:19.310 --> 00:28:22.310 align:middle line:84%
It's a value, we just compare
it against what we expect.

00:28:22.310 --> 00:28:25.670 align:middle line:84%
Now, what we expect in this
case is a fairly complex object.

00:28:25.670 --> 00:28:27.830 align:middle line:84%
This is an array
of text objects.

00:28:27.830 --> 00:28:31.190 align:middle line:84%
These are just values, again,
they don't have any behavior,

00:28:31.190 --> 00:28:33.640 align:middle line:84%
and they're a mixture of
color data and text data.

00:28:33.640 --> 00:28:36.920 align:middle line:84%
So you can see the first
one sets the color to green,

00:28:36.920 --> 00:28:39.962 align:middle line:84%
then has my name, then
a one-space gutter,

00:28:39.962 --> 00:28:41.420 align:middle line:84%
then sets the color
to the default,

00:28:41.420 --> 00:28:42.878 align:middle line:84%
and then has the
text of the tweet.

00:28:42.878 --> 00:28:46.000 align:middle line:90%


00:28:46.000 --> 00:28:51.530 align:middle line:84%
So testing all this stuff is
very easy and quite painless.

00:28:51.530 --> 00:28:54.367 align:middle line:90%
Now, this is not a large system.

00:28:54.367 --> 00:28:55.700 align:middle line:90%
It's about 1,000 lines of code--

00:28:55.700 --> 00:28:57.760 align:middle line:90%
933 lines of code.

00:28:57.760 --> 00:29:00.140 align:middle line:84%
362 of those are what I
would call infrastructure.

00:29:00.140 --> 00:29:03.020 align:middle line:84%
This is stuff like I had to
write a selectable queue that I

00:29:03.020 --> 00:29:04.880 align:middle line:90%
could pass into io.select.

00:29:04.880 --> 00:29:07.370 align:middle line:84%
I had to write a
little actor library.

00:29:07.370 --> 00:29:11.030 align:middle line:84%
I had to wrap some libraries,
like the Twitter gem--

00:29:11.030 --> 00:29:12.493 align:middle line:90%
sort of one-time stuff.

00:29:12.493 --> 00:29:13.910 align:middle line:84%
But after you get
passed that, you

00:29:13.910 --> 00:29:16.640 align:middle line:84%
have about 200 lines
of imperative shell,

00:29:16.640 --> 00:29:20.150 align:middle line:84%
you have 367 lines
of functional core,

00:29:20.150 --> 00:29:21.810 align:middle line:90%
and it is a strict wrapping.

00:29:21.810 --> 00:29:24.050 align:middle line:84%
The core only interacts
with the core,

00:29:24.050 --> 00:29:27.200 align:middle line:84%
and the shell organizes
the core along

00:29:27.200 --> 00:29:29.810 align:middle line:84%
with the stateful
pieces of the system.

00:29:29.810 --> 00:29:32.480 align:middle line:84%
And then you get
362 lines of test,

00:29:32.480 --> 00:29:35.113 align:middle line:84%
and that is only for the
core and one small piece

00:29:35.113 --> 00:29:36.030 align:middle line:90%
of the infrastructure.

00:29:36.030 --> 00:29:38.330 align:middle line:90%
The shell is actually untested.

00:29:38.330 --> 00:29:40.610 align:middle line:84%
The only conditional
in the shell

00:29:40.610 --> 00:29:42.800 align:middle line:84%
is the case
statement that we saw

00:29:42.800 --> 00:29:44.360 align:middle line:90%
that was choosing between keys.

00:29:44.360 --> 00:29:46.880 align:middle line:84%
There's no other decision
made in the entire shell,

00:29:46.880 --> 00:29:49.430 align:middle line:84%
so I don't even really
feel the need to test it.

00:29:49.430 --> 00:29:52.003 align:middle line:84%
I fire the program up, I hit
J. As long as that worked,

00:29:52.003 --> 00:29:53.420 align:middle line:84%
pretty much the
whole thing works.

00:29:53.420 --> 00:29:56.750 align:middle line:90%


00:29:56.750 --> 00:30:01.810 align:middle line:84%
So that is the structure of
this actual system that I built,

00:30:01.810 --> 00:30:06.970 align:middle line:84%
which brings us to one last
very brief topic, which

00:30:06.970 --> 00:30:10.970 align:middle line:84%
is the connections between
this and other ideas

00:30:10.970 --> 00:30:14.420 align:middle line:90%
in software development.

00:30:14.420 --> 00:30:17.320 align:middle line:84%
I would not be standing here
giving this talk if I had built

00:30:17.320 --> 00:30:20.320 align:middle line:84%
a 1,000-line program and
found that it was good,

00:30:20.320 --> 00:30:22.930 align:middle line:84%
and therefore, this is the
correct way to build software.

00:30:22.930 --> 00:30:25.010 align:middle line:90%
That would not be reasonable.

00:30:25.010 --> 00:30:28.630 align:middle line:84%
What I've really done is
go through other ideas

00:30:28.630 --> 00:30:31.990 align:middle line:84%
in the industry and tear
them apart and rephrase them

00:30:31.990 --> 00:30:35.590 align:middle line:84%
in terms that we use
in 00 programming.

00:30:35.590 --> 00:30:37.710 align:middle line:84%
The first of those
ideas is the IO monad

00:30:37.710 --> 00:30:39.460 align:middle line:90%
in a language like Haskell.

00:30:39.460 --> 00:30:44.080 align:middle line:84%
The imperative shell is a quite
obvious analog of the IO monad.

00:30:44.080 --> 00:30:48.190 align:middle line:84%
In fact, when I returned
that array of lines

00:30:48.190 --> 00:30:50.920 align:middle line:84%
from the tweet renderer,
in a sense what I'm doing

00:30:50.920 --> 00:30:54.380 align:middle line:84%
is the same thing that an IO
monad is doing in Haskell.

00:30:54.380 --> 00:30:58.480 align:middle line:84%
It's a value representation of
an action that should be taken.

00:30:58.480 --> 00:31:01.030 align:middle line:90%


00:31:01.030 --> 00:31:03.580 align:middle line:84%
You should also see shades
of hexagonal architecture

00:31:03.580 --> 00:31:05.920 align:middle line:84%
or the ports and
adapters pattern in this.

00:31:05.920 --> 00:31:10.030 align:middle line:84%
The imperative shell is
really just a thin layer

00:31:10.030 --> 00:31:12.610 align:middle line:84%
of adapters into all
the stateful services

00:31:12.610 --> 00:31:14.980 align:middle line:84%
to allow me to write
functional code without having

00:31:14.980 --> 00:31:17.590 align:middle line:84%
to worry about that terrible
world of things that are slow

00:31:17.590 --> 00:31:20.410 align:middle line:90%
and fail.

00:31:20.410 --> 00:31:24.490 align:middle line:84%
And finally, I don't think it
would be reasonable to expect

00:31:24.490 --> 00:31:28.630 align:middle line:84%
to write a 50,000-line program
that's a 45,000-line functional

00:31:28.630 --> 00:31:31.030 align:middle line:84%
core surrounded by a
5,000-line imperative shell.

00:31:31.030 --> 00:31:33.130 align:middle line:84%
I think that would
be ridiculous.

00:31:33.130 --> 00:31:37.180 align:middle line:84%
What probably actually happens
in a well-designed system

00:31:37.180 --> 00:31:39.370 align:middle line:84%
is that you have lots of
functional cores surrounded

00:31:39.370 --> 00:31:40.900 align:middle line:84%
by imperative cells
that communicate

00:31:40.900 --> 00:31:43.270 align:middle line:90%
with each other destructively.

00:31:43.270 --> 00:31:46.690 align:middle line:84%
And this is exactly what
is going on in Erlang.

00:31:46.690 --> 00:31:49.297 align:middle line:90%
This is the actor model.

00:31:49.297 --> 00:31:51.130 align:middle line:84%
Erlang is a functional
programming language,

00:31:51.130 --> 00:31:54.728 align:middle line:84%
but sending and receiving
messages is not functional.

00:31:54.728 --> 00:31:56.020 align:middle line:90%
That's a destructive operation.

00:31:56.020 --> 00:31:58.390 align:middle line:84%
Popping a message
is destructive,

00:31:58.390 --> 00:32:01.150 align:middle line:84%
and the bang operator in
Erlang that sends a message,

00:32:01.150 --> 00:32:02.770 align:middle line:90%
that's destructive.

00:32:02.770 --> 00:32:04.720 align:middle line:84%
So I imagine a larger
system being composed

00:32:04.720 --> 00:32:06.430 align:middle line:84%
of many functional
cores surrounded

00:32:06.430 --> 00:32:08.800 align:middle line:84%
by layers of scar
tissue to protect them

00:32:08.800 --> 00:32:10.270 align:middle line:90%
from the outside world.

00:32:10.270 --> 00:32:13.540 align:middle line:90%


00:32:13.540 --> 00:32:17.100 align:middle line:84%
And finally, just to
summarize what I think

00:32:17.100 --> 00:32:19.170 align:middle line:84%
happens when you build
software this way--

00:32:19.170 --> 00:32:20.130 align:middle line:90%
testing is easy.

00:32:20.130 --> 00:32:21.900 align:middle line:84%
I can tell you
firsthand that's true.

00:32:21.900 --> 00:32:24.330 align:middle line:84%
And testing functional code
is almost always easier

00:32:24.330 --> 00:32:27.230 align:middle line:90%
than testing imperative code.

00:32:27.230 --> 00:32:29.310 align:middle line:84%
The tests are fast
naturally, without having

00:32:29.310 --> 00:32:32.760 align:middle line:84%
to do any weird stuff
like stubbing and mocking.

00:32:32.760 --> 00:32:35.610 align:middle line:84%
There are no call boundary
risks because you're not

00:32:35.610 --> 00:32:37.860 align:middle line:84%
stubbing and mocking, so
you have real boundaries.

00:32:37.860 --> 00:32:39.490 align:middle line:84%
The boundaries
happen to be values,

00:32:39.490 --> 00:32:41.310 align:middle line:84%
but you're using real
values in your tests

00:32:41.310 --> 00:32:43.890 align:middle line:84%
so you know that your calling
methods actually exist,

00:32:43.890 --> 00:32:46.415 align:middle line:90%
for example.

00:32:46.415 --> 00:32:48.540 align:middle line:84%
Concurrency gets easier--
at least the actor model.

00:32:48.540 --> 00:32:51.060 align:middle line:84%
Probably other models, but
I don't think a whole lot

00:32:51.060 --> 00:32:54.600 align:middle line:84%
about other models
of concurrency.

00:32:54.600 --> 00:32:56.595 align:middle line:84%
And finally, that's a
special case of the fact

00:32:56.595 --> 00:32:58.470 align:middle line:84%
that you get higher code
mobility in general,

00:32:58.470 --> 00:33:01.650 align:middle line:84%
between classes, between
subsystems, between processes.

00:33:01.650 --> 00:33:04.350 align:middle line:90%


00:33:04.350 --> 00:33:06.238 align:middle line:90%
So that is the final section.

00:33:06.238 --> 00:33:08.280 align:middle line:84%
Now you know what all
these silly little diagrams

00:33:08.280 --> 00:33:11.400 align:middle line:90%
were on the initial slide.

00:33:11.400 --> 00:33:12.540 align:middle line:90%
My name is Gary Bernhardt.

00:33:12.540 --> 00:33:15.600 align:middle line:84%
I run Destroy All Software,
which produces screencasts.

00:33:15.600 --> 00:33:18.810 align:middle line:84%
If you want to see me
actually demonstrating

00:33:18.810 --> 00:33:20.907 align:middle line:84%
that Twitter app
live in more depth,

00:33:20.907 --> 00:33:23.490 align:middle line:84%
there's a screencast on Destroy
All Software called Functional

00:33:23.490 --> 00:33:26.230 align:middle line:84%
Core Imperative Shell, which
was the first place I ever

00:33:26.230 --> 00:33:28.230 align:middle line:84%
talked about this in
public, and I go through it

00:33:28.230 --> 00:33:30.593 align:middle line:84%
and show you exactly
how it works.

00:33:30.593 --> 00:33:32.010 align:middle line:84%
And with that,
thank you guys very

00:33:32.010 --> 00:33:34.910 align:middle line:90%
much for listening to me talk.

00:33:34.910 --> 00:33:44.000 align:middle line:90%
