Rewriting Rack: A Functional Approach
This is a modal window.
The media could not be loaded, either because the server or network failed or because the format is not supported.
Formal Metadata
Title |
| |
Title of Series | ||
Number of Parts | 69 | |
Author | ||
License | CC Attribution - ShareAlike 3.0 Unported: You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal and non-commercial purpose as long as the work is attributed to the author in the manner specified by the author or licensor and the work or content is shared also in adapted form only under the conditions of this | |
Identifiers | 10.5446/37793 (DOI) | |
Publisher | ||
Release Date | ||
Language |
Content Metadata
Subject Area | ||
Genre | ||
Abstract |
|
Ruby Conference 201724 / 69
2
5
8
11
13
19
20
21
32
33
34
35
37
40
43
44
52
54
55
56
57
58
62
64
65
67
00:00
Functional programmingFunctional programmingTwitterFunctional programmingJSONXMLUMLComputer animation
00:33
Data managementSoftwareSquare numberComputing platformCoroutinePunched cardSet (mathematics)Computer animation
01:04
Virtual machineComputer programmingFeedbackMultiplication signSoftware bugNeuroinformatikDifferent (Kate Ryan album)Punched cardSelf-organizationIntegrated development environmentInteractive televisionMcCarthy, JohnComplex (psychology)Wave packetElectronic mailing listBit
02:21
System callProgramming languageSlide rulePunched cardSoftwareFunctional programmingWeb 2.0Computer animation
02:57
Inheritance (object-oriented programming)Software frameworkRuby on RailsWeb applicationBuildingComputer animation
03:28
Software developerComputer programmingLibrary (computing)Programming languageRoutingVacuumRight angleComputer animation
04:09
Error messageElectronic mailing listWordProgramming languageComputer programmingException handlingVariable (mathematics)Inheritance (object-oriented programming)Computer animationMeeting/Interview
04:46
Finite element methodBlock (periodic table)Computer programmingLevel (video gaming)CodeElectronic mailing listReal numberAlgebraic closureRight angleBookmark (World Wide Web)Local ringContext awarenessMeeting/InterviewComputer animation
05:27
IntegerMultiplication signPrimitive (album)Rule of inferenceIntegerSocial classComputer programmingObject (grammar)TheoryType theoryRight angleGastropod shellComputer animation
06:11
System callUniform boundedness principleCodeFunction (mathematics)Object (grammar)Programming languageObject (grammar)MappingMassAlgebraic closureNumberCore dumpSystem callRight angleFunctional programmingComputer programmingFunctional programmingProjective planeResultantProcess (computing)Parameter (computer programming)Symbol tableCodeMultiplication signDisk read-and-write headQuicksortMessage passingAreaVirtual machineMeeting/InterviewComputer animation
08:34
Software testingConvex hullFunctional programmingMathematicsVarianceFigurate numberVector spaceFunctional programmingVariable (mathematics)Algebraic closureKey (cryptography)Greatest elementComputer programmingKeyboard shortcutLevel (video gaming)Computer fileMobile appSymbol tableTouchscreenPosition operatorResultantLine (geometry)CodeEntire functionHash functionProgramming languagePoint (geometry)Identity managementParameter (computer programming)QuicksortMereologyIntegerMappingRight angleMatching (graph theory)WordSystem callBlogClient (computing)BuildingImplementationElectronic mailing listCompilation albumComputer animation
12:04
Point (geometry)Perspective (visual)MathematicsPoint (geometry)Perspective (visual)Object (grammar)BitFunctional programmingWeb applicationAlgebraic closureWeb 2.0Google ChromeWeb browserData managementServer (computing)Right angleProcess (computing)Video gameComputer animation
13:11
MathematicsWeb 2.0Parameter (computer programming)Server (computing)CodeVulnerability (computing)Ruby on RailsImage resolutionComputer animation
13:52
Dependent and independent variablesCache (computing)Programmable read-only memorySystem callInterface (computing)Web 2.0Server (computing)Rule of inferenceSystem callCartesian coordinate systemRight angleMiddlewareChainInstance (computer science)Different (Kate Ryan album)Multiplication signClient (computing)WordCountingDependent and independent variablesLine (geometry)Mobile appQuicksortUniform resource locatorHash functionStructural loadMereologyCache (computing)Social classWeb applicationFunctional programmingGreatest elementTouchscreenBlock (periodic table)Algebraic closureFunctional programmingExterior algebraKey (cryptography)Lambda calculusCodeContext awarenessNumberParameter (computer programming)Point (geometry)Error messageValidity (statistics)Computer fileStack (abstract data type)MappingVariable (mathematics)Selectivity (electronic)Uniformer RaumWater vaporReduction of orderString (computer science)Line codeLibrary (computing)EmailCASE <Informatik>Casting (performing arts)ACIDMetropolitan area networkRoundness (object)Amsterdam Ordnance DatumFlow separationBitFood energyComputer programmingWindowFlock (web browser)AreaProper mapMathematical optimizationComputer animation
23:12
Directory serviceCodeState of matterQuicksortPoint (geometry)Line (geometry)Endliche ModelltheorieFunctional programmingNeuroinformatikWindowWordType theoryObject (grammar)CountingSoftware bugComputer programmingState of matterProcess (computing)CodeInheritance (object-oriented programming)Computer fileRight angleSoftware developerBlogAmsterdam Ordnance DatumMeeting/InterviewComputer animation
24:28
TorusSoftware bugComputer programmingMiddlewareState of matterFunctional programmingMessage passingProduct (business)2 (number)Roundness (object)Physical systemSystem callResultantKey (cryptography)Game controllerHash functionShape (magazine)CodeVariable (mathematics)Instance (computer science)Reading (process)BuildingFile formatBit rateRight anglePhysical lawData managementSet (mathematics)SpacetimeProgrammer (hardware)Parameter (computer programming)Continuous integrationComputer animation
27:48
Nichtkommutative Jordan-AlgebraExecution unitObject (grammar)Inheritance (object-oriented programming)Web 2.0Server (computing)CodeInstance (computer science)Thread (computing)MiddlewareMultiplication signSocial classFunctional programmingDatabaseSystem callProcess (computing)Software bugNeuroinformatikWindowProduct (business)Row (database)1 (number)Programming languageUsabilityScaling (geometry)WordRight angleDatabase transactionPoint (geometry)Single-precision floating-point formatWebsiteMessage passingWeightSoftwareAnalogyDigitizingComputer programmingAlgebraic closureMeeting/InterviewComputer animation
30:20
CalculationBasis <Mathematik>InformationParsingIntegrated development environmentInterface (computing)WindowNeuroinformatikSource codeWeb 2.0State of matterCartesian coordinate systemFunctional programmingMultiplication signInformation securityPersonal identification number (Denmark)String (computer science)Interface (computing)Object (grammar)Data structureException handlingIntegrated development environmentMobile appCountingHash functionSystem callProduct (business)FreezingMiddlewareInstance (computer science)MathematicsMappingDifferent (Kate Ryan album)Key (cryptography)Associative propertyRight angleNumberEndliche ModelltheoriePrisoner's dilemmaContext awarenessObject-oriented programmingServer (computing)Goodness of fitPointer (computer programming)WordCellular automatonWeightComputer programmingSocial classBit rateLie groupMeeting/InterviewComputer animation
34:30
Large eddy simulationProduct (business)Right angleMathematicsCASE <Informatik>Information securityRootCausalityMeeting/InterviewComputer animation
35:04
Group actionMultiplication signInstance (computer science)Social classHash functionElement (mathematics)System callComputer animation
35:36
AbstractionSoftwareStructural loadFunctional programmingAlgebraic closureMultiplication signHash functionFreezingObject-oriented programmingProgramming languageFunctional programmingLibrary (computing)NumberElectronic mailing listDifferent (Kate Ryan album)Computer programmingRight angleSpeech synthesisObject (grammar)Line (geometry)ImplementationComputer animationDiagram
37:54
Coma BerenicesXML
Transcript: English(auto-generated)
00:13
Welcome to my talk, Rewriting Rack, a Functional Approach. Thank you so much for coming. I'm super stoked to be here, and I'm
00:21
glad you guys all made it. I'm Alex Wheeler. You can feel free to tweet me, ask me any questions, comments, or concerns at Ask Wheeler and see what I'm working on at Alex Wheeler on GitHub. I currently work at VTS. We do commercial real estate asset management software. We have just over 6 and 1 half billion square feet
00:42
managed on the platform, and it's all powered by Ruby. But I started thinking, right? And if we had built VTS, say, in 1956, we definitely wouldn't have used Ruby. We might have used something like this. We might have used Fortran, right? This is a Fortran punch card. And what you would do here is you'd
01:02
punch in an instruction set. You'd hand it off to a huge machine, like this IBM 704, and a few hours, days, years later, you might get a result, right? Given that you had no bugs, you didn't spill any coffee, you didn't rip your program, you'd get some feedback. And this was cool, right?
01:21
This was high technology at the time. A few years later, we have John McCarthy. Now McCarthy's the father of LISP, and McCarthy's work was a little bit different. So Fortran, it worked super well for these large organizations that they kinda knew exactly what they needed to do.
01:41
Their problems weren't that complex. And McCarthy's work, he was doing artificial intelligence. So it wasn't so much that he even needed the solution, he just really needed to find the questions to ask. And for that, he needed a much more interactive computing environment. He needed to be able to ask questions and get instant feedback.
02:01
He couldn't wait to go hand off a punch card to a machine so what he did, he went and he built LISP. And with LISP, we got the first REPL. So if you've ever opened up an IRB session or Rails console and gotten that instant feedback, that comes directly from LISP, the technology implemented in the 50s.
02:21
A few years later, we have Grace Hopper. And Hopper led the team that built the COBOL programming language. And if you've never seen COBOL, here's a simple function. It looks a little different than the punch cards we saw a few slides ago, right? It reads just like English. And this was a huge idea for her.
02:42
She wanted to build software using languages that read just like English. Let's fast forward. 1993, the public web came out. We also learned earlier in the keynote, this is when Matz first started working on Ruby. Few years later, the first public release of Ruby.
03:01
Super cool, almost 25 years ago. And in 2004, we see Ruby on Rails. This is probably when many of us first started writing Ruby, right? We were building web applications in Ruby using the Rails framework. And here we are today.
03:20
It's 2017, and you guys have all decided to come and watch me speak for 40 minutes, which is super cool. And the future's now, right? Like we've figured out software development. We write perfect programs, they never crash. And you know that's a lie, right? You're all laughing. That's a total lie, we haven't.
03:41
We don't really even know what we're doing. The industry is just way too young. But what we do have is we have Ruby. And the genius behind Ruby is that it wasn't built in a vacuum, right? Matz actually looked to the past at some of these technologies we just went over, and he combined them all into this language that we decided to come together in New Orleans
04:02
and talk about for a few days. When I tell my friends that, they think I'm crazy, but we really enjoy it. So what were some of those influences, right? Well, Smalltalk, we see influences from LISP, we talked about IRB, Perl, and even Python.
04:21
So has anyone seen any of these before? Well, not if you've written perfect programs, but if your program's ever raised an exception, you've probably seen some of these. And if you weren't aware, these are actually Python exceptions. These come word for word from the Python programming language. So we can thank them for that. Or how about some of these? These are our cryptic global variables.
04:42
And we did borrow these from another language as well, right? For better or worse, we got them from Perl. Super cool. And if you are gonna use them, please, require English. You'll get much better references. They're no longer cryptic globals, they're just globals. And blocks.
05:01
If you've done any Ruby programming, you've probably iterated over some kind of collection with map or each, and you've passed it a block to yield each value into to do something with it. And this comes directly from LISP. From the 1950s, we have real closures in Ruby, right? We have a block of code bound to some locals
05:21
that we can take and we can execute it in different contexts. And last but not least, this is probably one of my favorites. I remember when I saw this for the first time. This goes back to what Grace Hopper was talking about with really readable programs, 10 times, do something, right? But what does it show? This shows that in Ruby,
05:40
we don't really have true primitive types, right? Everything is an object. We talked about in the talk before that there are no rules in Ruby, and that's true, right? We could go in, we could open up the integer class, we could redefine the plus method to actually be minus,
06:01
and in our new world, three plus one equals two. And that could either be really exciting for you or really scary, but it's pretty cool, and that's actually an idea that came straight from Smalltalk. Smalltalk was a first language where everything was an object, and we took that. And we have Matt, who took all these amazing ideas
06:23
and baked them together into one extremely fun program language. Now, we look at these, right, and we start thinking, what do they all have in common? So maybe not this one, but the rest of them. They all aim to simplify.
06:41
We don't build new programming with your technologies for fun, some of us do, but for some of these larger projects, like languages, we're trying to make our lives easier. And Matt's talked about this earlier, right, when he said, I wanna push more onto the machine and have the humans do less. And that's because programming's really tough. If anyone tells you differently, they're a liar.
07:03
This is a really hard profession, and we're only human. And you've probably heard this number. It's a plus or minus seven, right? It's the number of things that we can keep in our head at one time. So I have a question for everyone. In how many ways can we call this proc?
07:21
So let's go through them, right? We can call it and pass it one. This gives us the same exact result. We have a little more syntactic sugar. You can call procs this way as well. You can send the call message with argument one. We could be a little stricter. We could use public send, so if it was private,
07:42
it would blow up. And procs don't enforce the area of the function, right, of that closure. So we could pass any number of arguments, and it's still gonna return the same exact result and ignore the rest, really similar to like a JavaScript function. And this sort of takes us to this idea of functional programming, right?
08:01
Functional programming really, at its core, is about two ideas. It's about organizing our code around functions instead of objects. And arguably more importantly, it's about having immutable data. And you might not even know what those two things mean, but that's okay because there's a much bigger idea at play
08:21
with functional programming, and it's to simplify. It's the same thing we've seen since the 50s. We've been building technologies to simplify how we build our programs because we know programming is really tough. And that brings us to closure. So I kept hearing about this functional programming idea
08:40
from friends and colleagues and talks similar to this and blog posts, and I didn't get it. It sounded like this crazy advanced concept that you would only use if you're building, I don't know, a distributed blockchain mumbo jumbo. And I was never gonna use it to build my CRUD apps. So I said, you know, I need to go figure out what's this thing all about?
09:00
And I looked at closure, which is a functional language, it's also a LISP, that's implemented on the JVM. And let's go through a few examples, right? Because what happened was I went over to closure land, and when I came back to Ruby after re-implementing a lot of my programs in closure, it clicked and I said, I get it.
09:20
By using these functional languages, we get rid of an entire array of problems. They are literally impossible to encounter in these sorts of languages. So let's look at some basic closure. You're gonna learn some closure today. So here's a basic closure file. You'll see that we have vectors. So a vector's just like a Ruby array, right?
09:40
Here we have a vector of four integers. We have maps, where key value pairs, very similar to a Ruby hash. We can call functions. So if you've never seen a LISP, please go experiment with one, because they're the most interesting programming concept ever. But when we want to call a function, we wrap our code in parentheses.
10:02
So here we're gonna say, give us the value keyed by name. And if you look in the bottom left corner of the screen down here, you'll actually see the result of the line of code that I'm on. So here we get back Alex. We can go down, and we can do basic math. So we execute this, and we get two. We can have nested function calls, right?
10:21
So here we're multiplying two by two, and we're getting four. Here's a basic function definition. So we're defining a function called full name. It takes two arguments, first and last, and then it concatenates them together. And we can go see what that looks like, right? We're gonna hug it in parentheses. That first symbol on the left side, full name,
10:41
that's in function position. So that's always gonna be a function, and anything after it are arguments. Now, that's not the most interesting part, though. That looks like many other programming languages, right? So here we have a var. This is kind of like a Ruby variable. We're gonna say, we're gonna bind to conf this map. And let's see what we wanna do.
11:01
We wanna change the name from RubyConf, where we are, to ClosureConj. So let's see what happens, right? So we associate with conf the names key to ClosureConj, and what do we get back in return? We get back a new map that says name ClosureConj. But then we go ask that var for its identity,
11:22
and this says like, what are you, who are you? And we see, it's still RubyConf. And this is the key point to closure, and a lot of functional programming is immutability. You cannot change a variable once you've assigned it a value. We're working with values. You cannot.
11:40
The language will not allow you. Let's see one more. Here we have a vector of two maps, RubyConf, ClosureConj. We have pop, very similar to Ruby. We get back RubyConf. We check the identity, and it's still that full vector, right? And this is the key idea here. So that's the 101 to closure. If you wanna learn more, I highly encourage it.
12:01
We can talk after the talk. Now I love this quote by Alan Kay, right? Alan Kay's the one that gave us that idea of everything is an object. He says a change of perspective is worth 80 IQ points. And so by the end of this talk, what I really hope is that we all become a little bit smarter. What we're gonna do is we're gonna go in
12:21
and we're gonna examine Rack from the perspective of a functional language like Closure and see how can we avoid really simple problems, like problems that don't exist in some other languages. And it doesn't matter if you don't know what Rack is, because I'm about to tell you. So Rack is super simple.
12:40
On the left, we have a web browser, Google Chrome. And on the right, we have a basic Ruby web application. A lot of us use Rails, right? And in the middle, we have this thing, Unicorn. And Unicorn is a web server, and it has the most boring job in the world. It sits there all day and waits for requests to come over the wire. And then it hands it off to a web application.
13:02
The web application does something with it. It then hands it back to the web server, which knows how to parse it back into plain text and send it over the wire. So plain text in, and we get plain text back out. And there's not just Unicorn, right? There's many web servers. There's Unicorn, there's Puma, there's Webrick.
13:21
They all do the exact same thing, but they have their pros and cons, right? They have their strengths and weaknesses. And as we know, there's not just Ruby on Rails. There's Sinatra, there's Hanami, and there's probably thousands or countless other home-baked solutions. And what would be really cool is if today we were using Rails, but tomorrow we wanted
13:41
to use Sinatra, and we didn't have to change really any code that interfaced with the web server. Or today we're using Unicorn, and tomorrow we want to use Puma, we can just swap them out willy-nilly. This is what Rack solves. Rack is a web server interface. And it has a few very simple rules. It says if you're gonna be a Rack-compliant Ruby web app,
14:00
the entry point to the application where you're gonna get this request, you're gonna define a method call. It takes the request as an argument, and if you're a Rack-compliant web server, you're gonna call that method and pass it the request as a Ruby hash. Now, Ruby library, you need to return a triple, so an array with three values. The first value being an HTTP status code,
14:22
the second being a hash, which are key value pairs representing any headers, and then the third being the body that responds to each. As long as you do that, we can all work together and swap in and out, right? So Rails, Sinatra, Hanami, your basic Rack applications, they all have this entry point, which is call. So what's the most basic Rack application we can build?
14:42
This is pretty easy. We have a class called app. We could call it anything we want. We could call it Alex, we could call it Ruby. We define a call method which takes the request, which Rack likes to call the env. You could call this anything, call it request. And it returns a triple, right? And how would we run this? Well, we require Rack.
15:00
We initialize a new instance of our app so that it can respond to call. Now we say we pick a unicorn. So we pick this adapter. We say, hey, we're gonna use the unicorn web server, and we run our application. And if we wanted to use Puma, we just swap in the Puma adapter. If we wanted to use WebRic, we just swap in the WebRic adapter. So assuming this code is in a file called server RB,
15:22
you could call it whatever you want, we can run it, and we'll see here. Sweet, that's a Ruby web app in three lines of code. That's pretty awesome. But we could do better, right? What also responds to call in Ruby? Well, procs do.
15:41
A proc responds to call. It can take arguments, and it can return a triple. So that means that we could actually build a Ruby web application in one line of code. And this does the same exact thing. Now we can also use a lambda. Lambdas are procs, but they enforce their arity. So we have to pass the correct number of arguments, or an argument error will be raised.
16:01
And this is the same exact thing. But it's pretty cool, right? We can build a Ruby web app in one line of Ruby. Now, Rack gives us another alternative if we wanna go a step further. It gives us a DSL for crafting these. So we get this RackBuilder class. And what we can do is we can initialize a new builder. We can say, hey, we wanna use this app.
16:22
And then we're actually gonna run that. Now, you're probably looking at this, and you're saying, Alex, that doesn't provide anything better than our proc we were using. Well, the difference here is that we can use this thing called middleware. So we can say, hey, use the logger middleware, and use the cache middleware. And middleware, it's also a very simple idea.
16:43
At the bottom of the screen down here, we have our Rack application. And outside, on the top, where we can't see, we have a web server listening for requests. And along the way, between our web server receiving the request and our Rack application receiving the request, we might wanna do some stuff. We might wanna log something. We might wanna actually just go to our cache
17:01
instead of ever even hitting the Ruby app. We might wanna do loads of things. And how it works is it's like a stack. A request comes in. Our web server's parsed it into a Ruby hash. It gets passed to the first middleware. It logs something. It gets passed to the next middleware. In this case, this middleware actually attaches something to the request. So maybe the URL to some cached asset or something.
17:22
That gets handed off eventually to the Rack application, and it responds. So let's look at how middleware's implemented, right? It looks very similar. We have a class. We're calling it middleware. And it has to define a call method taking the request, just like a Rack app. But the difference is it needs to know about the next middleware or Rack app in the stack.
17:42
And so it gets initialized with the next application. So how that works is that we initialize the next application. Every time a request comes in, it calls the call method, passing it the request. We can do something with the request, and then we just pass it on to the next thing in the stack, where it's eventually gonna end up at a Rack app,
18:02
which will respond, and it'll get sent back over the wire to the client. And that got me thinking, right? What is this Rack builder really doing under the hood? If you go do a word count on it, it's 171 lines. But there's actually a lot of really good comments. So without comments, it's 79. Still like a fair amount of code.
18:21
And so let's go through. Let's actually see what it's doing. Here, we have a new method. We have use, we have run, and we have to app. So we have these sort of four main methods. Here's the actual code. We're gonna spend the rest of the talk. No, I'm just kidding. Don't even worry about reading that. We'll go through a simplified example. What does initialize do?
18:41
It makes an empty array. That's where we're gonna store all of our middleware that we keep wanting to use in our stack. Every time you call use, it takes the middleware, puts it in a block, and pushes it onto that array. When we say run, that says, hey, we wanna use this Rack application that responds a call and returns that triple. And then to app goes through,
19:01
and it actually constructs the stack. So here's the actual code. It's just a reduction. We're gonna go through this right now a little bit slower, but what it pretty much does is it waits for a Rack application, and it's gonna pass it on down that chain until we get a fully composed Rack app,
19:20
and then we can pass that to the web server. So let's see what that actually looks like, right? Let's imagine we're using two middleware, logger and cache. So we've called use twice. Our array has two values. So we have logger and cache, and they're waiting for that Rack app because we need something to be the tail end of the stack to finally respond to the client.
19:41
So what do we do? Oh, okay, we have a Rack app, and it gets passed to cache. So now cache gets initialized with our Rack application, and then that whole stack, which is the whole composed stack so far, gets passed in to logger. So now logger knows about cache, and cache knows about the Rack application,
20:00
and here's our built app. And I love playing with syntax and having fun with programming, so if we rearrange this a little bit, we can, it'll still be valid Ruby, but we're gonna move things around a bit. We move cache down, and we move our Rack app down, and you'll see this is our actual stack, right? It maps one to one. There's our logger, there's our cache, and there's our Rack app,
20:20
and that's what's gonna happen when a request comes in. So that begs the question, right? Can we do functional programming in Ruby? And I think we can, right? We have the tools to do it, and if we're gonna use functions instead of classes, could we implement this without any classes, right?
20:41
Well, we could, and how we do that in Ruby, we don't have first class functions we can pass around, but we do have this idea from Lisp, right? We have these blocks, and we have procs, right, which are local variables bound to a context that can be executed in any other context. So let's see how we can do this, right? Here's our logger. So we need an initialize method.
21:02
Okay, we'll just use a proc, pretty simple. Initialize takes an app. Well, our proc, it could take an app. What do we need? We need a call method. Okay, well, procs respond to calls, so we just return another proc. We need to take in the request, so we take in the request.
21:21
What do we need to do? We need to call that app. Well, we can do that, right? We have a closure around it. Now we can give it a name. The class has a name, our logger has a name. We can enforce the arity now, so we're gonna use a lambda instead of a proc. So here we go. Here's our middleware implemented without any classes.
21:44
We're just using procs. Are we safe in here? Who thinks we should keep going? Okay, we're gonna keep it going because we just implemented this without any classes
22:01
and everyone's about to have their mind blown. So we can do the same exact thing for the cache middleware. We can do the same thing for our app, right? And we have our builder. We've just implemented this with just using procs, and that's kinda cool, right? We could pass our app to say we wanna use the WebRook web server,
22:22
and we could actually run this. And then we could start having fun with it, right? Ruby, we learned in the last talk, there are zero rules in Ruby. We can replace our do ends with C style blocks. We can actually use this other syntactic sugar way to call a proc. We can actually wrap everything in parentheses. Ruby parentheses do not matter.
22:43
We can compact it, right? To make it a little more concise. And then what we can do is we can compare it to the closure solution. And it looks nearly identical, right? Let's just pull out the middleware part. So here's our logger and cache. This is closure. Just kidding, this is Ruby.
23:00
I got some of you. I saw some people like, yeah, he's right. No, this is Ruby. And let's actually look at the closure. It's almost identical, right? So now you're thinking, you're like, Alex is out of his mind. And I might be a little out of my mind. I like having fun with this sorts of stuff. But I actually think there's a point here, right?
23:20
Do we need that DSL? Do we need Rack Builder? Do we need 171 lines? What if we could just go and delete them? That would be really awesome, right? What if we ran a word count and that file actually didn't exist? I gotta quote the late and great CTO, Carl Baum. He's the CTO of our company.
23:40
And he really instills this in everyone on the team. Code is a liability. We, as developers, we love to write code. But at the end of the day, if we could solve something without writing code, that's a real win. So we just saw one, right? We can implement all these things without this DSL. Maybe if we program in a more functional style, we could eliminate some code, right? By just using simple functions.
24:01
But let's look at some more. The two I wanna look at are shared mutable state and also functions instead of objects. And the first one, shared mutable state. This one's super interesting, right? There's lots of bugs that are really, really hard to track down and really annoying. Like bugs where you just kinda wanna go home and take a nap, right?
24:21
And then there's other bugs. The type of bugs where you say, I'm quitting my job and gonna go soul search for a while. Or I'm gonna throw my computer out the window. And these are bugs that come about because of shared mutable state. So let's look at some real world examples. Like these are really simple problems that we can run into in our Ruby programs
24:41
that would be impossible to run into if we were using a functional language. And we're gonna do it by building some Rack middleware. So here we have some middleware that has a call method, right? And let's think of a crazy scenario. We're building this for our courthouse, right? And we have a verdict come in and we need to format it into a message to send off to some system or to the judge, to the jury to read off the results of the trial.
25:03
So what we wanna do is we wanna actually update the request. So as that request comes in, we wanna add a new key to the hash. We wanna say, okay, the verdict message is equal to whatever the verdict message function returns. So we need to find a verdict message which takes in a verdict which we're reading off the request, right?
25:22
And it's very simple. It says if the verdict's guilty, return is guilty. If it's not, then return is not guilty. And that's all good. And then product comes around. They go, Alex, Alex, Alex, Alex. Alex, Howard, we don't know their name. Oh, we need a name, okay. So the request also has a name attached to it. So we take in the name now
25:40
and we say if the verdict's guilty, name is guilty. Otherwise, name is not guilty. Sweet, I'm going home, going surfing. And then product comes back, right? They say, Alex, you gotta come back from the beach. There's another problem. God, not again, what do you want? And it turns out we forgot that they might actually have a Mr. Oh, no, no, no, no, no.
26:02
That's the next example, excuse me. So then we forgot that they're actually sending a full name. One second. Did you hear that? Can we get a big round of applause for the social?
26:26
Thank you, man, who just said that for looking out for us.
26:41
Thank you, you told us that, thank you. Sorry about that. Anyways, so then what happens is that they're sending in full names and they just want the first name. So what do we do? Well, we split on whitespace and we take the first value. We take their first name, it's all good.
27:02
And then they have Mr. and Mrs. and Miss and this code is getting out of control. So what do we do? And this is a pretty common thing because as programmers, we want to be lazy, right? Well, we just say, okay, we're gonna make a new method, we're gonna extract a method that's just gonna deal with formatting the name, right? Because the shape of this
27:21
is starting to look a little scary. And what's the easiest way to do that? Well, we don't want to have to pass name down all these functions. So we just abstract an instance variable and then we can just call that, right? So we still have our verdict message, but we have a new helper method called name, which takes no arguments, it just, we have this global instance variable to this instance that we can read off of.
27:42
It passes code review, it passes continuous integration, it gets merged and what do you do? Big corporate high five, right? Look over and give them the high five. And it's all going well. We can actually see an exam player, we're actually running this, I took a gif of this, it's kind of fun. You'll see, we'll put a name, we'll put, and it should return
28:01
what the formatted message is. So here, Alex is innocent, Alex is not guilty, and Jordan's guilty. Jordan's also sitting in the front row right there and he's one of my coworkers. And that's all great, right? Super simple. But then we're using Rack, right? We can use more than one web server and we're getting lots of users at this point,
28:21
so we switch off of Webrick and we go and use Puma. And our code's still passing, right? Everything's all dandy, but Puma's cool because when we scale, it's multi-threaded. Requests don't have to wait for the request ahead of them to finish. They can just start running. And let's see what happens here. This time we're gonna run it with two threads.
28:42
And I've also introduced a little sleep functionality to sort of mimic a long-running, maybe database transaction, so one of our requests is gonna get stuck and the other one's gonna be able to keep processing because it's gonna get handled on a different thread. Let's see what happens.
29:02
So this one's gonna wait. So we get Jordan's not guilty. We should get Alex is guilty. We get Jordan is guilty, right? And this was all good in CI, this was all good locally, but what's the problem here? These are one of the bugs that makes you wanna throw your computer out the window.
29:21
These are the bugs I was talking about. Let's figure out what the problem is. In Ruby, we get an object ID for every single instance of an object. We get a unique ID. So what we can do is we can actually just, we're gonna log that and let's run our program again. We'll see on the right, we'll see it getting logged. Okay.
29:40
Do you notice anything very interesting about this? They're the same, right? They're the same exact ID and the problem is here in this name. When you construct your middleware, your middleware gets initialized once. So that class gets initialized once, but call will get called every single time a request comes in.
30:01
This is very easy to gloss over and I've made this mistake before. In Ruby, we go to production and we just built some really horrible software. We might've just sent someone to jail by accident, but in something like Clojure or functional language, this is literally impossible. It's impossible. The language will not allow you to do it.
30:22
And what do we do? We throw our computer, not out the window. We break it, we're done with software. We're going home. But we have solutions. We have lots of solutions. Here's a very common solution to this problem in Rack middleware. Every time a request comes in, we're just gonna dupe the object. So we're gonna dupe that middleware,
30:41
make a new instance of it. We're gonna copy it and then call a helper call method. And that's fine, right? It works. We don't have the shared middle state anymore. We're working with different instances every time a request comes in. But then you go look in the Rack source code and you say, how many times did they call dupe? 29. And that's 29 times that they remembered to call dupe.
31:03
Who knows? We might have missed a few. Think about your application. The Rack source code's pretty small. I know at VTS, we have a massive application. How many times we've forgotten to call dupe? I'm not gonna even go there. And how do you know if you forgot to call dupe? Well, we have a solution for that. It's called freeze.
31:21
So in Ruby, you can freeze an object and if you try to mutate it, it actually raises an exception, right? It says you try to mutate this frozen object. And Rack Builder, that DSL, it says you can call freeze app. And what it does is when it's constructing your application, it goes down and freezes every single middleware so that if you do mutate something, an exception is raised instead of sending
31:40
someone else to jail, right? And that's cool. And then you go back to the Rack library, right? And we go run a word count. We get 96 times they call freeze. And there's various reasons for this, right? We talked about having mutable strings in Ruby, there's performance benefits to doing this, but 96 times, and once again, that's 96 times that we remembered to call freeze.
32:03
That's horrible. So let's look at another example. Functions instead of objects. So data, right, if you think about data, it's just facts. It's just a thing. It doesn't really have behavior, but what we do to it in object-oriented land, we attach behavior to it.
32:21
We're like, oh, Alex's name is Alex, but it has behavior, like change name, and no, it's just Alex, the name is just Alex. It doesn't have behavior. So what do we do, we want to model a person, right? We make a class, we give it an adder reader name, and we initialize it with a name. You want to make a company, we do the same exact thing, pretty straightforward. We can make instances of a person, of a company.
32:43
We can update the person's name, so we have a method update name, and we give it Al, and we check, we check that person's name, and it's Al. Now we update the company's name, and what do we get? We get a no method, right? This method's undefined, because what happened is that your coworker went,
33:02
and he called, or she called this method something totally different, and here we see it, right? It's impossible to enforce this. Everyone has different styles of programming, and what Clojure says is we don't have classes. We're gonna use very simple data structures. Mostly, we're gonna use maps. Maps are great. It's a great associative data structure.
33:22
We have keys that point to value, so here we have a person with the name Alex. We have a company with the name VTS. If we want to update Alex's name, we use a source. If we want to update a company's name, we do the same exact thing, and that's not a very concrete example, but let's look at one. So we saw that hash that Rack gives us,
33:41
a Rack web server, and we have another abstraction. We can use the Rack request, and what this is, it's a convenient interface to a Rack environment. So what it looks like is we just initialize it with the request, and we can do stuff with it. So what we have here is, this is like the Equihax middleware, and what this does is it goes,
34:02
and we want to actually scrub out any social security numbers that people are putting in, and how we do that is we have a private method called sensor social security number, and we just update, right? So we can call on request. We can get its params, which gives us a hash back, and we say set the social security number to the censored social security number. So if you give a social security number like this,
34:20
I hope this isn't anyone's social security number, you get back a censored social security number, and it's all good, right? We go to production again, and what do we do? We celebrate. We're like, hell yeah, bring in the interns, you know, like this is great, and here we go. We go into production. We're going live. Let's see what happens.
34:43
Oh my gosh. That's the social security number. That was supposed to be censored, and it's not, and once again, if that's your social security number, I'm really sorry. Go change it. So what do we do in this case, right? We go to jail. Like, you're going to jail. Like, you're going to jail.
35:00
Like, you're going to jail. I'm going to jail. This is out of control, and why are we going to jail? What is the root cause, right? Request params returns a hash. I know how to update a hash. I learned this the first day I learned Ruby. No, they've gone, and they've said, hey, you need to call update param, and I was there playing around IRB, and it seemed to be working. I was getting a hash,
35:20
but no, that actually returns a copy of the hash, and that's fine if you're expecting that, but we're not expecting that. We need to go learn this new API every single time we want to work with some instance. So what do we think about this, right? What do we think about these classes when we could use simple data? Like, no, let's not use that. That's confusing. That's tough.
35:41
How about person? No, we don't need that. Company, no, and the madness. What's a better abstraction, right? We have hashes. They're like closure maps, right? They're associated data structures. They're very powerful. Want to represent a person? Cool, hash. A company, hash.
36:00
Rack request, 487 lines. I'm sure we wouldn't be able to get rid of all of those. We'd have to pull some of them in if we wanted to use these pure functions, but maybe we could. 96 times freeze in the Rack library. How about zero, dupe, 29 times.
36:21
What's a better number? Zero. Zero is easy. You don't have to do anything. So the question is, does OO solve all of our problems? No, it doesn't. We just solved. We solved tons of problems that we could totally avoid by just using a different language.
36:40
But will functional programming solve all of our problems? I don't think so. Probably not. If you were going to ask me today, what is the future of these languages, I think we're going to find somewhere in the middle, right? I think that object-oriented programming is a great way to design software and reason about it. But if we could get some of these benefits from functional programming, we'd end up in a really good place.
37:05
So what I challenge everyone here to do is to go out, right? You need to go look at these other communities. If we're going to build better software or one day actually know how to build software, we're going to have to look elsewhere. We have an amazing community here with loads of talented people.
37:21
And there's lots of other communities with loads of talented people. And the only way we're going to do it is going to be by looking at those communities. But most importantly, we need to have fun doing it, right? Most of us got into this industry because we thoroughly enjoy programming. I wouldn't be up here speaking or doing weird stuff like implementing Ruby as a LISP
37:40
if I didn't find it extremely fun. So while you go out and you're searching these other communities, have a lot of fun with it. And thank you. That's all I got.