- Alright.
Good morning, everybody.
- [Audience] Morning.
- That was more
enthusiastic than I thought.
I thought everybody but me
went out to drink last night.
Alright, and welcome to my keynote.
Ruby versus The Titans of FP.
I'm gonna talk a little bit about myself,
so that you know where
I'm coming from with this,
'cause this is gonna be more of a story.
So I've been a hobbyist
programmer for about a decade.
The very first thing
that I ever programmed,
I was 12 years old.
Who here remembers QBasic on like MS-DOS?
Who here remembers Gorillas?
My very first collating of code
was to cheat in that game.
I made my brother always play Player 2.
And I made his win values
display something other than
what they are, just a little
bit to the left or right,
so that he can never hit me.
And that began a lifelong
love of programming.
I'm also fascinated with
functional programming.
It's a very mathematical discipline
and it's very easy for me to
reason about what I'm doing.
However, I'm relatively
new to the industry.
I'm at my first job at Mavenlink,
a little company you've probably heard of
from the three other speakers
that have been wandering around here.
And I've been there for eight months
and it's my first real
experience with Ruby.
And so, while I've experienced Haskell,
and Clojure, and JavaScript,
it's only really this
year that I got into Ruby.
Yet somehow I snuck on
to the stage of RubyConf
as a keynote speaker.
I'm still trying to figure that one out.
Alright.
So when I first started learning Ruby,
my motivation was like,
"Oh hey, I need to do this for my job."
But I also wanted to drag
all of my functional knowledge with me.
And not many people
use Ruby in a functional manner.
If you want to create something,
you have an object for it
and it has methods and
everything's available,
and it was just like,
"Okay, I'll roll with it."
Until a few months ago
when somebody poked me to do a CFP.
And I finally decided
to actually explore it
and I was like,
"Okay, I'm gonna make functional
programming work in Ruby."
And this talk is a documentation
of my past three months,
distilled, so that you don't have to
see me yelling at a rep while trying
to figure out what's broken.
Alright.
So, welcome to Ruby
versus The Titans of FP.
Before we get started,
we wanna talk about what
functional programming is
because not everybody's familiar with it.
Now if you ask 20 people,
they'll give you 20 different definitions.
Some old people for math.
Some old people...
Some people would bring out
their Bible of Category Theory
and sit you down for a three hour lecture.
I like to be able to be a
little bit more pragmatic.
So to me, I like to describe it as this.
It's a programming paradigm
that focuses on functions
as transformations over generic
objects and data structures,
as opposed to objects that we model.
A good example of this is a login object.
I think everybody here
has written a login object
if they've worked on some web app.
And usually when you
approach it, you're like,
"Okay, I'm gonna give it some attributes
"and it's gonna know its internal state,
"but then it's also gonna
know how to validate itself,
"it's also gonna know
how to delete itself,
"so it's gonna know how to
save itself to the database."
Functional programming
separates those two things,
so you can still have a login object,
which can end up being a generic
data structure just like,
"Okay, I know the user
I'm associated with,
"I know if I've been deleted at."
But then you'll have a
separate set of functions
that can operate on that object.
For example,
to invalidate it.
So why is that a good thing?
Well, one, it's easier to reuse logic.
And I think this is one of the biggest
benefits for it.
I'm pretty sure everybody's
ran into the situation where
they're like, "Oh hey, this class has
"an amazing method that
I really wanna grab."
But I can't sub-class
it for whatever reason,
because inheritance just
scares me sometimes.
And so, it's really nice to
have that already separated,
so that you can then figure
out how to make a generic
for an entire class of objects.
Unless you compose smaller
units of logic together,
which will be an entire 10
minutes on this presentation.
And it can,
keyword "Can" in giant air quotes,
result in cleaner and
more performant code.
If done the right way.
If done the wrong way,
it can get kind of scary.
Alright.
So these are my five key features
of a functional programming language.
This is probably contentious if you go
to like the functional
programming lexicon,
there's like 300 definitions inside of it.
But these are the five things
that are important to me.
Higher-order functions,
currying, composition,
functional purity, and immutability.
We are only gonna talk
about three of those today.
Functional purity and
immutability are things that
we can do
through the data structures that we use
and through how we treat
the flow of our program.
However, higher-order functions,
composition, and currying
tend to be things that are more primitive
in our languages and if we
don't have support for them,
we end up losing a lot of things.
So, today I'm gonna compare
Ruby versus Clojure,
Haskell, and JavaScript
in order to see if we have those things
that make other functional
programming languages nice.
And we're gonna start with a
little thing called Clojure.
Now, Clojure was born in 2007.
And it is of the Lisp family
and that makes some people very happy
and that makes some people very sad.
So, what makes it a functional language?
It has higher-order functions.
It has first-class
functions, which just means
that we can take function
and stuff it into a variable
and then use it anywhere we want.
And then its standard library
has immutable data structures
and this makes me so happy.
And I wish I had more chances to use it.
This is an example
of what a function call
in Clojure looks like.
It's just a list.
The first thing in the list
is a function.
Everything else is arguments.
Lisp is actually short
for list processing.
And this seems elegant
and simple and nice,
but then you end up with programs
that look like this.
This is a lazy take.
And those parenths at the
very bottom of the slide
either make you make a face like that cat
or you put on a robe and you become one
of the old wizards of Lisp.
But we're not gonna dive into
anything crazy with Clojure.
We're gonna talk specifically about map.
Going back to our definition.
Map is a function that takes arguments.
What are those arguments?
Well, the very first thing
is a function that we want
to transform some collection.
The example that we're gonna use
and that you're going to be sick of
at the end of 35 minutes
is going to be increment.
We're gonna increment and
decrement so many numbers,
it's gonna be great.
And then the second thing that we pass in
is a collection,
which is gonna be a standard
array for a purpose.
And so, this is what it
ends up looking like.
We map our increment
function over 1 through 5.
It returns 2 through 6.
And we've already arrived
at the first capability,
which is higher-order functions.
A higher-order function is a function
that either takes in other functions
as part of its arguments
or returns a function as its result.
The example being our
map taking in a function
for its transformer.
Can we use this in Ruby?
Obviously.
I'm pretty sure everybody's
just like, "Why?"
Everybody knows we can do this.
So, the standard way
of doing a map in Ruby
is to have some collection call map on it
as method in them pass in arguments.
We're gonna create something
a little bit more generic
for reasons that will
slowly become clearer
as we walk through this.
So we're gonna start with a proc.
And give it our two arguments.
Function as our first argument,
which makes this map a
higher-order function.
And then our collection.
And then we're going to cheat.
We're just gonna call the
collections, map method.
If it quacks like a duck,
it must work for us.
And then, this is,
if you've ever read the
Clojure documentation,
f and coll are used all over the place.
And this is what they actually stand for,
function, collection.
Congratulations, you can now read 90%
of the Clojure documentation,
because that's all it is.
It's all signatures.
So, how do we use this?
Well, here we're gonna
create an increment.
That just takes in a number
and returns one to us.
We are going to
give ourselves a list
of numbers, 2 through 4.
And then, we're going to use map.call
in order to invoke it with our arguments.
This drives me insane.
Fortunately, Ruby is nice to us
and we can hide it.
This was a mind-blow like two months ago
when I was learning.
And I was like,
"Okay, I'm going to get tired
"of using call anywhere.
"Oh, there's syntactic sugar for it."
It's good.
And this is at the interpreter level,
so if you define an object
that has a call method on it,
it has this alias on it as well.
And so you can hide all your calls.
It looks kind of beautiful.
The dots still throw me off,
because I want them to
look like method calls,
but it works out.
A more complex example,
going back to the logins
that I keep talking about,
is like,
"Oh, hey, we're gonna create
an anonymous proc that..."
Oh, not really anonymous,
we're stuffing it with something.
We're gonna create a
proc that takes a login
and sets it to deleted_at the Time.now.
And we're gonna say,
"Okay, is deleted_at not null?
"Cool, we deleted something."
And then we're gonna
create a list of logins
or a set of logins,
or a vector of logins.
I have not found a use for vector
in actual production code yet.
And we're going to map over that
with our invalidateLogin helper.
And so, we've met our
first requirement here
that we could use higher-order functions.
And this syntax is a little bit different
than what I'm used to,
but it's there.
And so, the next thing we're
gonna cover is currying
and we're gonna turn to
our old friend/enemy, Haskell.
Haskell was created in 1990,
so it is one year younger than me
and I officially feel old now.
And it's from a family
of languages called ML,
which is short for metalanguage
which I always thought
was machine language
until like two days ago.
Had somebody corrected me on this.
And the thing that make
it a functional language
is that everything is
curried inside of it.
Everything is pure
and it has the most beautiful type system
that I have ever seen in a language.
Although, it also drives
some people insane.
So what does a function
call look like in Haskell?
Well, here we're gonna
create our increment function
by calling add, which is just
gonna be a plus sign here,
with 1.
And then we're gonna take that increment
and then pass it into map
with 2,3,4
and then we get 3,4,5 as we expect.
But what was with that inc?
Add takes two numbers
and we only gave it one,
and it didn't blow up and
give us an argument error.
This is strange.
Let us investigate,
in the worst possible way.
We're gonna diverge a little bit
and talk about
Hindley-Milner type systems,
which might be a little bit heavy-handed,
but will help us figure
out what's going on here.
So, we can be here talking
about them forever,
but two things we need to know are...
There.
That it can be used to express
the signature of a function
and that Haskell uses it to annotate types
and it will check against
the types that you annotate.
An example,
add, or at least add as we're used to.
We have a function name here
and then we take two arguments here
and we just group them in parentheses,
so say that we're taking
them at the same time.
We have a function arrow
which tells us we're going
from something to something
and that gives us a result.
Congratulations, you all can now read
type signatures in Haskell.
For the most part.
Alright, so this is how
we're used to doing it.
We call add with two numbers,
it gives us one number,
it yells at us if we give it...
It yells at us if we give it one number
and blows up, and we get
all sorts of weird things,
and then we get a 30 mile long sack race
and that's never fun.
Haskell does things a different way.
So, Haskell still has an add function
and it still has the same behavior
where it takes in two numbers
and it gives us back a number.
But we can only give it
one argument at a time.
And when we give it an argument,
it will return to us
a function, waiting for
the rest of its arguments.
And so let's go back to our inc
and figure out what happened.
So we have add here and we give it 1.
And so we pass 1 in here
and you can guess what we got back.
A function waiting for the second number.
And this gets us to our
definition for a curried function.
And this is why you do not rewrite slides
at 10 o'clock at night before you present.
This slide right here.
I rewrote this three or four times,
hated the definition
and I just said,
"Okay, it's midnight, I'm not
gonna mess with this anymore."
So, a curried function is a function
that upon being applied
to less than the total set of arguments
that it's supposed to take,
returns another function
waiting for the rest of its arguments.
So our add function takes two things,
we give it one,
it returns a function waiting for one.
If we have a reduced
that took three things
and we give it two things,
it would return us a function
waiting for the last thing.
So on and so forth.
If we have ten and we gave it five,
it would wait for five more
and just give us a
function waiting for that.
And let's just do some
borderline dangerous things.
So we're gonna implement this in Ruby.
And we're going to use
our best friend, closures,
in order to do it.
So here we're gonna create an add
and we're gonna have a
proc that takes an x.
And when we call this with a number,
it's gonna return to us
a proc that waits for y.
When we call this, then
it will give us results.
So we're gonna go create our
friendly increment function
by just saying add.(1).
And it's gonna bind that 1 to x
and we're gonna get a
function that waits for y.
And we'll just add 1 to whatever y is.
So we can call immediately
and we get in 2 and we expect 3.
Or we can map it
and we get our incremental array
as we've gotten the entire time, I hope.
This is the easy slide.
So, there is a better way.
Nobody wants to write 20 nested closures.
Luckily, we have native currying.
This made me jealous
'cause I came from JavaScript
and we don't have native
currying in JavaScript.
So when I saw this, I was really happy
'cause then we can end
up writing our functions
like we normally do.
XYZ, do a thing,
have it automatically
curry-handled for us.
And in addition to doing all the things
that we can do with currying,
we can also just call it at any time
with all of its arguments.
Then we just get the
result back immediately.
So, a quick divergence in
the order of arguments.
You'll notice that Haskell closure
and our Ruby map all had
the same order of arguments.
And this is why.
If we wanted to create a
function called inc_map
that iterates over a list of numbers
and increments all of them,
we can build it this way,
assuming that our add and map are curried.
What if we wanted to
decrement everything instead?
Well, that's easy enough to do.
We use our add and just
give it -1 instead of 1
and build the function the exact same way.
And through currying,
we can build entire families of functions
off of primitives.
I could write mathematical
proof right now,
but it would not be good.
I can create every step
function from my add.
I can create every iterative
transform function from my map.
And that makes it easier for us
because then we don't have
to worry about those things.
Then we get to extract the layer away.
So we've got higher-order functions
and we've got currying.
What's left?
Our best friend,
JavaScript.
So, JavaScript
is relatively new.
In 1995.
And it doesn't really get
to be put in a family.
It's kinda like Scheme which is C syntax.
And so, it's not really
like anything else.
And so, we get to name it its own thing.
What makes it a functional language?
Well, first-class functions,
which we have determined
lets us do higher-order functions.
And closures, which
will let us do currying.
But past that, it
doesn't really have much.
It has really strong functional
programming libraries,
but not many things
built into the language.
And so, despite the fact that we have
all of these really low
level tools in JavaScript,
the core API doesn't
really like us doing things
and an FP style in some places.
Example: array.
So we create an array.
Three numbers.
Map is a method on array.
And so, if we wanna access map,
that's how we have to get it.
Pop gets something off
of the end of the array
and gives it back to us,
and it mutates the array underneath,
which destroys immutability
and doesn't really work out
for us in a functional flow.
Wait a second.
Doesn't Ruby do these same things?
It does exactly the same things actually.
Map is also a method
and pop also mutates.
And so, a lot of the stuff
in the standard library
that would create friction
for a functional programming flow
exists in both languages.
So that gives us a clue
because we can look at how
functional programmers in JavaScript
solve their problems,
in order to figure out how in Ruby,
I can solve my problems.
And the answer is functional
programming libraries.
In the beginning, there was Underscore
and it was okay, kind of.
But it got the order of arguments wrong.
Until Lodash came around.
But unless you use Lodash FE,
nothing was curried.
So Ramda came by and everything's curried
and everything's in the
right order of arguments
and everything has the Haskell
names for its functions
and it let's you pretend
that you're not in a browser.
And so, it ends up
being my favorite thing.
Out of all the things in Ramda,
my favorite thing is compose,
'cause this lets me glue things together.
How many people have ever done this?
You write a method,
it takes in a thing,
you invoke it,
and you save off the result to something.
And then you take that result
and you immediately pass
it into another function
and then you save off that result.
And then you go
pass, save, pass, save,
pass, save, pass, save,
until 30 years later,
you're finally able to return the thing
from all these functions
that you've been running.
There's a better way.
In this case,
Ramda gives us a nice compose.
Ramda also gives us all
these nice curried functions.
And so, I'm gonna define add2 here as,
"Oh hey, I'm gonna create
an incrementer with add(1)."
Glue those two functions together
and then the
argument that I'm passing
gets passed into one of the incrementers.
The output of that gets
passed into an x incrementer.
And so, we create a chain
or a pipe of functions here.
And so, our add2(2) returns 4.
This is a little bit more complex.
And if the type signatures
make you feel weird,
ignore them, they're not there.
They're slightly grayed out for a reason.
Here we're gonna create
something a bit more complex.
MapReduces.
So, if you wanted to do a MapReduce,
usually, you have
some function that takes in something,
call this map, saves off the result,
call the reduce, and
then passes that back.
Here, MapReduce are curried
because they're in
Ramda's standard library.
Map takes in an incrementer.
A transformer in a collection,
we give it a transformer,
it gives us a function that
waits for a collection.
Reduce takes in a reducer,
an initial value, and a collection,
and it returns a final value.
And because the types match
up for all of that stuff,
we get to just glue
them together like this.
This also lets us be point-free,
so that we never have to mention our data.
And if we never mention our data,
I can't mistype,
which is why I love it.
So, benefits.
We get actual easy logic reuse.
If we create an increment
function and a decrement function,
we can glue them exactly
two ways together.
But if we had 20 things,
then we could put those
together like Legos,
any way that we want.
And so we get actual reuse of logic
and we can do very strange things with it.
We can also compose our compositions.
And this makes for really easy refactors.
A really good example is a response for...
Request-response cycle.
I have a composition
that takes a request
into some internal state.
I have a composition
that does something
with the internal state
and then I have a composition
that takes the internal state
and maps it to the response object.
I can isolate those three separate things
and then compose them together
and make my entire request-response cycle
into a single function,
which ends up being really clean
when you have 300 routes.
So, how are we gonna do this in Ruby?
Like all things.
Here we're gonna create a binary compose
that just worries about two functions.
We're gonna create an outer proc
that takes in two functions.
And it's gonna return a proc
that waits for the arguments.
Once we get the arguments,
we're just gonna call y
with the arguments.
And the output of that is immediately
gonna be passed into x.
The reason that this goes y to x
instead of x to y
is because it works like
mathematical composition.
There's usually a pipe function
that does the opposite order
in most functional libraries.
And so we're gonna create our inc here
and we're gonna compose
it just like we did
in the JavaScript example.
And if we add 2 to 4, we get 6 back
in the same world.
If we wanna do this to
more than two functions,
then we can take the
compose that we just made
and create a variadic compose.
And here we're just gonna say,
oh hey, we're gonna take
in all of our functions
and then we are going to reduce over that
with our binary compose operator.
And that will do the exact same thing,
only with as many functions as you like.
So here we can create
an add3 from three increments.
Why we wouldn't just use
add3 to create our add3,
I don't know.
It works out for this.
And then if we add3 to 4,
we get 7 back.
Nobody wants to write this.
This is one of those steps of code
that you'll find yourself using everywhere
once you're used to it.
And so the next question is,
what if I just want a gem?
I wrote it.
And so, if you go to RubyGems
and you look up reductio,
it only has three functions.
Add, compose, and map.
'Cause those are the three functions
I need for this demonstration.
And it works.
The add is curried, as you can see.
And the compose is variadic.
So you can use it to glue
20 functions together.
Is that a good idea?
Never.
Can you do it?
Yes.
I have, it's not pretty.
Don't ever look at my
GitHub, it's a scary place.
Alright.
And so, this
is something I'm very happy about
because I've got
comfortable enough in Ruby
to create a functional library.
And so what have we
established Ruby can do?
Well, we set out to find
higher-order functions,
compositions, and currying
comparing it with three
different languages.
And we found all those things.
We haven't looked at functional purity,
we haven't looked at immutability,
but the tools that we have are enough.
And so, while people like to pit Ruby
against other functional
programming languages,
nothing stops it from being
part of the circle too.
And that's what I've got for you today.
(audience clapping)
I went through that way
faster than I expected,
which means I have question time.
So this is gonna be interesting.
So, next steps.
If you wanna learn more
about functional programming,
Dr. Frisby's Mostly Adequate
Guide to Functional Programming
is a book on GitHub.
It is written by that cute badger.
That is actually Brian Lonsdorf
who is a functional
program guru on Netflix.
All the examples are in JavaScript,
but it should be easy enough.
We also have my rebo for reductio
which is also a gem now.
I wanna get more discussion going
'cause I feel like I've
trapped myself in this bubble
trying to divide things the hard way
in order to try and learn things.
But now I wanna talk with people about it.
So if you're on Twitter,
tweet questions at me,
or just ask me them in
the next 10 minutes.
And then, tweet with the #functionalruby
and I'll be looking at it
and I hope other people
will be looking at it,
so we can start a discussion.
And then please help me.
I have never written an open
source gem before and...
Your heart is confusing.
Tara has convinced me
that I need documentation.
So, if you're a functional programmer
and you would like to help with that
or if you're not a functional programmer
and you wanna learn,
please, please come help me with this.
And before I'm done,
that's my contact information on the left.
I wanna give a shoutout to...
Transcord.
First, I wanna give a shoutout to RubyConf
because it has been a hard week for me
as a trans woman of color
and I have felt very welcomed here.
And I am very glad.
It's my first RubyConf too,
so I'm happy that I made it out here.
(audience cheering and clapping)
But if you know any trans people
that are looking for a sense of community,
the link up top
is a link to Transcord
which is a global support group
that would love to have more members.
And if people have not already
sold you on the collate,
that doesn't even have a link yet,
then we have this nice
engineering blog link here
that covers a bit of our culture.
And so, I think we are good for questions.
I was not prepared for this.
Yes.
Okay, so the question was that
they have troubling seeing the
benefits of point-free style.
And it kinda seems obtuse
and not indirect
because you never mention your data,
so you're never sure
where your data really is.
And so, one of the things
that really appeals to me
about point-free style is that
we get to walk away from
the constraints of our data.
With the MapReduce that
I demonstrated earlier,
it doesn't matter if it's an array,
or if it's a linked list,
or if it's a Maybe, or Either, or an IO,
or some other functor.
I don't worry about that.
I just know that there's an interface
that anything coming into this function
has to deal with.
And then that lets me use it generically.
And so that ends up being...
That ends up being part
of the benefits for me
because I can just say in
my type annotations like,
"Oh, this takes in a monad
and it returns blah."
And then I don't really have to
worry about my data pass out.
Does that answer your question?
Yeah, so it's kinda like duck typing.
So in JavaScript, it's
actually treated that way.
So if you use Ramda and you pass in
something that has a map method on it
as the mappable to map,
it will use that method on it.
And so you get something akin to that.
Okay.
So the question was...
And please correct me if I
just completely crush this.
Is that eventually you have IO,
eventually there has
to be some side effect.
All programs have side effects.
Otherwise, what are they
really doing, right?
And so, how do you handle that
in functional programming?
How do you...
deal with that when you
have basically, two styles,
or you have an object
defining it old style
that has to do these things,
but you also wanna create this logic
and functional style?
And if you went to the composition talk,
James actually had a really
good thing about this.
So go back and watch his talk.
But I'm gonna try and press this.
My first instinct is to just tell you
use an IO monad.
But that doesn't exist in Ruby yet,
I'm working on it.
That's one of the things where
right now in our company,
I'm on a team that's using a react.
And so, the way that I handle that
is that when I do compositions,
I push that to the very end of our chains.
Because compositions
are functions themselves
and you can compose
them with other things,
I can create a pure composition
that goes from point A to B.
And then in my actual app,
just compose that with something
that takes in data and
calls this a side effect.
You get to create a
very clear divide there
and say like,
"Oh hey, this is my pure code.
"Inside of this library,
everything is beautiful."
And then the side effects get
pushed to the edge of your app
and that's how I tend to handle that.
I have strong pains about
ActiveRecord scopes.
So, I wasn't able to hear
the second part after scopes.
Okay.
The question was,
how do I feel about ActiveRecord scopes
ignoring the rest of the library.
I like that scopes are
just kind of composing on each other
in order to build a SQL query.
I don't like that at any point,
somebody can cause a side effect
by doing something that
actually loads from database.
Database calls are one of those things
where it's like,
"Oh hey, I loaded up a user
"and I automatically
want you to database."
And I'm like,
"Eh, can I separate that stuff somehow?"
And so, when I write scopes,
I try to write the same way
I would write compositions
where I try and push anything
that would possibly load
as far down the chain as possible
instead of being like,
"Oh hey, I need to load
dissociation for this thing."
And then a database hit
occurs because of that.
Does that answer your question?
Cool.
I feel like I'm back in class.
I should be saying that
behind the podium, right?
Anybody else?
Yes.
Okay, so the question was,
have I done benchmarking,
because they have heard
that currying might...
What are words?
Words are hard.
It might impose the performance penalty.
And so, I have not done that benchmarking.
I wanna do that benchmarking
when I start fleshing out reductio,
just so that we can say,
"Oh hey, this is the performance penalty.
"You can do three less
operations a second,
"but it gives you such beauty."
And so that is a thing
that I also wanna work on.
I have not heard of it,
but I've also only been
at Ruby for eight months,
so my opinion might not
be always qualified.
Alright.
Does that answer your question?
Okay, that's good.
So, let's see if I
can go back to...
- [Man] What was the question?
- So the question was,
why do we not wanna
compose 20 things together?
Why do we wanna keep
our compositions short?
And this is...
That's actually a really
good demonstrator.
So, looking at the type
inquiries for this,
this is already kind of complex.
This is doing a lot of things.
It's iterating over a list,
it's applying a function
to everything in that list,
we get a new copy of the list back
with all of those things transformed,
and then we start folding over that list
in order to get a final value.
This encompasses a lot.
And if we were to start
adding new things on there
like we wanna map and do x thing,
we wanna map and do y thing,
we wanna map and do z thing.
Then it's really easy
to just look at a page
of composition and be like,
"What does this even do?"
I do this all the time.
And that ends up being a problem
because I'll go back to old code.
For example, something
I did for Hackathon,
I was like,
"Oh hey, I can bang this
on in five minutes."
Because it's just one giant composition.
But then when I go back
and review it later, it's like,
"What was I doing?"
It helps that there is a vocabulary
when you use a standard
functional library.
So you'll be like,
"Okay, this thing is head,
"so I grab the first
thing off of this array
"and then this incremented it,
"and then this impended
it to something else."
But it still gets to be
difficult once you get
past a certain length.
And because...
Compositions can be themselves,
made of compositions,
it's very easy to say,
"Okay, I have 20 things here.
"Let me grab these five, put
them in their own composition,
"and then replace those five with one line
"referencing that composition."
So it also gives you easier factoring,
which ends up being a beautiful thing.
'Cause then you can have your short
three, five line things,
but then still encapsulate
all of your functionality
into one single function
that gets passed and there's
a callback to something.
Does that answer your question?
Cool.
Okay, I've got four minutes left.
Anybody else?
No?
Last call.
Cool.
Thank you.
(audience cheering and clapping)
