 Welcome, how's everyone's morning? Cool. So my name is Brad Hirani. This is a talk about immutable data structures. Immutable data structures are an idea from functional programming. And I gave this talk because I got interested in this because I took a course on Coursera called the Principles of Functional Programming in Scala. It's taught by Martin Odersky, who's the inventor of Scala. Very, very, very great course. Highly, highly, highly recommend you take it. It got me thinking about how we can do some types of functional programming in Ruby. And what do I mean by functional programming? Functional programming is the techniques that are used to isolate state change, basically. And it's what you get when you combine a number of techniques and also use some discipline. It's what you do and also what you don't do. Ruby has a lot of the stuff that you need for functional programming built in. Immutable data structures has that yellow squiggle there because we get that with a third party gem. Higher order functions would be like passing lambdas around. Partial functional application, that's like currying. We don't do that a lot in Ruby. We do have lazy sequences as of Ruby 2, recursion. You can always recurse in Ruby. We don't have lazy evaluation and pattern matching. So if you combine all this stuff, you kind of get functional programming. But the truth is, most of functional programming is a poor fit for Ruby. Ruby is an object-oriented language. And I have tried to do pure functional programming in Ruby and you can do it. But I don't really recommend it, at least not in production. But these immutable data structures, I thought were really, really interesting because they are a piece that we can take that I think do have some really, really interesting use cases and also are just a really, really neat topic with some really cool aha moments in them. So we do have this immutability built in a Ruby. We have this .freeze method. So we take this array, we call .freeze. And then what happens is if you try to change this array, if you try to update, add an element or delete an element, this will raise an error. So the reason we have this is to prevent certain types of bugs. So if you have this array and you're passing it to some function that the success is predicated on this not changing, you can do this to avoid certain bugs. If you jump into the Rails source code, you'll often see string.freeze, right? That's a performance enhancement. It's a memory saving device. Freezing an array does not have the same performance enhancement as far as I know. So it's for not running into certain bugs. I guess that's useful. It's not really that interesting and it's not what this talk is about. That's because this data structure is immutable but it is not persistent. These are two different things. Persistent is a data structure with some special qualities and that's what I'm gonna be explaining today. So we get persistent data structures with a third party gem. It's called Hamster. It's a really, really awesome gem. Here I've made a list just containing the items one, two, and three. Hamster is really, really awesome. It's this great gem. It's got this whole collection of persistent immutable data structures. Is anyone here like a maintainer or has anyone worked on it? Maybe not. Well, if you're watching my video later, shout out to you. This is a great gem. Great work. So we get this list one, two, and three and what actually is the difference here? Let me show you really fast. So make sure this is big enough. All right, so if I make an array in Ruby, one, two, three. So there's L. L is one, two, and three. So if I do L two equals L, dot unshift zero. And unshift if you don't remember is actually the list operation that pushes something onto the front of an array. I get zero, one, two, three. Okay, what is L right now? What happens if I print out L? Is it one, two, three? Is it zero, one, two, three? It's zero, one, two, three, isn't it? Why is that? That's because both of these references, L and L two, have a reference to the same array. There's only one array in memory and they both point to the same one. So editing it in one place, changing it for all references that have it. Hamster is a little different. Hamster list. So if I do L equals hamster list, one, two, three. There we go. All right, it's not about that. All right, so I have L which is one, two, three. And if I say L two equals L, dot cons, and cons is the same as unshift. It pushes something onto the front. Why is it called cons? It's tradition from functional programming, dating back 40 years. But same thing, I cons something onto the front. I get zero, one, two, three. What is L now? L is still one, two, three. So that's the difference here. That's the difference between just a normal array and a hamster list is that when I edit hamster list, anything with the reference to the original stays the same. It remains unchanged. That's why it's immutable. So a persistent data structure is immutable, but immutable data structures are not necessarily persistent. Persistent data structures have these special qualities. So how do they implement that? All right, let's think about this for a second. Like say you wanted to make that hamster list class yourself. How are you gonna go about and do that? Well, the first thing we can think of is, all right, when I update the list, I could just clone it, couldn't I? I could just make a whole dang copy right in memory and then I would achieve what I wanted, right? No problem, that makes sense. Interesting example from everyone's favorite language, PHP, right, is that PHP actually does this. If you take this array, which is set to two, three, you can actually swap references like that. Like just set array two equals array one, then you can add four under array two, and actually array one is still unchanged, right? This is called copy unwrite. The PHP runtime is kind of smart. Oh my God, did I just use smart talking about PHP? Oh, what's wrong with me? But yeah, it actually clones things under the hood, keeps tracking these references, and clones things under the hood for you. That's kind of interesting, but it's not very efficient, isn't it? That's not very fast. You wouldn't expect that to be very performant. So how would we implement that immutable array, that persistent array, in a way that's performant, efficient and fast? It turns out a functional persistent list is actually a linked list. Did any of you ever take an algorithms class or something where you learn regular stacks, and queues, and trees, and linked lists, and you're like, wow, why would I ever use a linked list? Well, it turns out they do actually have a use case, and that's in this. So we have this list. So if I do this, I take one, two, and three, and then I use cons to push something onto the front. You can see that it's persistent. I have one, two, three, and I have a new list. What I've actually done is this. So I have a single linked list, and I have two references to the same list, to two different nodes in the same list. That's actually awfully clever, isn't it? Because I didn't have to clone anything. The one, two, three part are shared by the two lists, only to the references list and new list. It looks like two entirely different lists. It looks immutable, doesn't it? That's actually really clever because it's fast. It's performant, it's fast. It's a neat implementation detail. Okay, so straight from Wikipedia, persistent data structure is a data structure that always preserves the previous version itself when it is modified. Such data structures are effectively immutable as their operations are not visibly updating the structure in place, but instead always yield a new updated structure. Okay, cool. So a few limitations of this list, as you might expect. First of all, it's singly linked. Why can't I make this a doubly linked list? Think about that for a second. If I have this, and if I tack zero onto the front, if I wanted to make it doubly linked, I'd have to add a reference from one to zero, wouldn't I? If I do that, I'm changing it. Is that still immutable? No, so I've violated immutability, so it has to be a singly linked list. I can't go backwards either, because it's singly linked, I can only traverse this list in one direction. Also, I can only add to the front, can't I? So that's the limitation of this list, and I can't do random access. So a hamster list, you literally cannot say, all right, give me element three. Why not? Why can't I get element three? Well, what's element three in this list? Is that relative to the first reference? Is that relative to the second reference? It's kind of a relative thing, what element three in this list is, isn't it? So it's a relative question that we can't really answer. It doesn't really make sense. Also, even if you did implement it, you'd have to recurse to be bigger than, it wouldn't be very fast. All right, so another example here is my rock band linked list. So I've got this rock band, VH1980, consists of David, Eddie, Alex, and Mike. Then I do this, I take VH1980. This is a reference to Van Halen, the rock band. I don't know if you all have got my joke. They swapped out lead singers in 1984. Okay, so I take VH1980.tail. That tail gives me everything after the head, which is Eddie, Alex, Mike, and I conned Sammy onto the front. So here I swapped David with Sammy, haven't I? So now I have this in memory, because I've taken David off and added Sammy, I've got two references. I'm starting to sort of build up a tree here. To each of these references, it looks like a list because you can only traverse one direction, and I'm basically building up a linked tree. And we can even take this further. I can take this A, I add foo and bar. To foo, I add brisket and ribs. To bar, I add chicken and sausage. We can end up entirely with a linked tree, and we can have a reference to each node in the tree. And just so you know, my inspiration from this is that I'm from St. Louis, we have awesome, amazing barbecue. I moved out to California three months ago. They don't even know what barbecue means. It's this like grilled tri-tip. It's not the real thing. I'm really excited to be here in Texas because I got to eat barbecued ribs, the real kind. But anyway, so I've got this tree now, but the arrows only point up because I can only traverse one direction, which means to the references in the nodes, it looks like a list. Okay. Could we make a persistent graph? So a graph does not have the limitations of a tree. In a graph, everything can basically link to anything else. It's like the free-for-all of data structures, isn't it? Well, let's think about this second. If we started connecting nodes in a graph and you allowed traversing both ways, you'd have to doubly link. That violates immutability, but we probably could do it if the arrows all went one direction, couldn't we? Yeah, so if it was a directed graph and it would kind of ruin our directed graph if we made the circles, wouldn't it? Because at some point we'd have to modify something. So if we had to directed a cyclic graph, we could make this a persistent data structure because we'd only adding things on the end. Does this remind you of a tool you all use? Maybe something you know and love and use every single day? Let me yell it out. Woo! Yes, Git is basically a persistent data structure on disk, isn't it? It follows these principles, right? One way traversal, singly link lists, perfect history. Yes, you all use this every day. Git is a persistent data structure. So let's solve something a little bit more interesting. Do something a little bit more interesting, rather. Ha, ha, ha, ha. That part's coming later. Okay. Maybe, no, I'm gonna do it now. All right. So I'm gonna pull up a little game here. Come on. Oh yeah. So this is my isomorphic functionally, functional reactive JavaScript app. Just kidding, it's a flash game. This is called Bloxers. I first ran into this problem when I took the class that principles, functional programming in Scala class. I ran into this problem. It's a really, really cool one. So Bloxers is this video game, right? And I am that block right there. And the deal is I have to kind of move around and try to get that block to land in that hole right there. This is the whole gist of the game. Woo! All right, very, very cool. I did not make this game. What I made is a solver for this game. So this level has these like little bridges. I did not implement the bridges in my simulation. But we have this little game here. And it turns out this game can teach us a lot about persistent data structures. Let's see if I can do this. This is always the hardest part of my talk. All right, maybe this. Oh, and then I'm like, foods and steaks flashed. Oh my God. All right, let's try this again. This is the part where the nerves get me. Oops, no, I can do this. Watch. All right, all right, cool. Woo! That's the hardest part of my talk. So memorize this board of a second. Take a look at this board. Kind of memorize the shape. You got it. You've got to suck in your brains. You know what it looks like. All right. So here's my sort of ASCII representation of that board. The ASS is the starting point. The g is the goal point. Let me make that a little bigger for you. The g is the goal point. The zeros are the board, right? So I made a little ASCII representation of this. And my program is a little solver. Two. My program, what it does is it spits out the, oh yeah, it spits out the quickest solution to this problem. So it's gonna give me, it's a cheating game. It's gonna give me the fastest route to the goal there. Just to prove it to you that it works. Make that a little wider. This is the second hardest part of my talk, is making this all line up. All right, ready? Here we go. Wait a minute, that's not right. Okay, so right, whoops. Right, up, right, right, right, up, left, down. Right, up, up, right, right, right, down, down, down. Right, oh it froze again. This is why we got a real flash. Can you not see it? Oh. All right, wow, it really froze on me big time. Anyway, right, oh, there we go. Yeah, all right, right, up. All right, so I made a little solver. It finds the quickest answer to this problem, right? It's a video game solver. So how on earth did I do that? What does that have to do with persistent data structures? All right, first of all, all right. So if we think about this problem, we can model basically the moves in this game as a tree. So I'm up at the start. I have four possible moves. I can either move left, right, up or down. So that's that second fear in the tree. That shows me what my possible first moves are. So if my first move is right, then I have four other possible moves. I can move left, right, up or down, right? If my first move is up, I can move left, right, up or down. So that second row in that, or that third row, that bottom row in that tree represents all my possible second moves. So I can model my game using a tree and just a little digression here. The algorithm I do this is a functional version of this, which is kind of cool. It's a breadth-first search. I want to find the most efficient solution. So I want to evaluate all the possible first moves before I evaluate all the possible second moves. So it's a breadth-first recursive tree search. And the way that I do that is the functional version. So I take my first node and I flat map it to four directions, which gives me a list of four items. Four items I can flat map to left, right, up and down, which gives me a list of 16 items that makes my second row. So just a little reminder when I'm talking about flat map here. So imagine one, two, three and four. These represent the nodes in a level of my tree and I map them to four directions. I get 16 more directions. So I'm flat mapping between levels. I take the previous level. I can catenate it to the one before it. And I end up with an algorithm that basically flattens this tree. It's a breadth-first tree-flattening algorithm that allows me to treat these trees. It's one long, continuous list so that I can search through the list and find the answer. Find the one where the block goes in the hole. If I lost you there, don't worry. This is where the persistent data structures come in. When I get to the end of this, I find the answer, right? I have to be able to print out the history. I have to be able to print out the answer of how I got there. So I need to keep a history, otherwise I can't print out the answer at the end. Additionally, I also have to keep a history because I have to check for blocks, positions that I've already visited so I don't get stuck in an infinite loop. So how do I keep that history in a way that's efficient and fast? Well, this looks kind of like a linked list, right? So what's happening is like, say I get to this green node right here, the one that's highlighted that says right, I'm gonna map this to four directions. There are four possible moves I can take from that. Each of those four moves needs a different history list, doesn't it? I've already got a history list consisting of two elements. Now I need four new ones. Well, this kind of looks like what I had before just kind of turned on its side, doesn't it? So basically what I do is I cons a new history cell onto the front of the list and I get four new history lists but because it's a linked list I don't have to clone anything. So here I'm using the linked list to keep the history in my algorithm so that I can print out the answer at the end of my blockster's game. Interesting and novel uses of these persistent data structures. Interestingly, persistent data structures are also thread safe. So you may imagine that if I wanna solve this problem absolutely as fast as possible I might wanna spawn off threads. Maybe at the first level where I've got four moves, my MacBook has four cores. I probably wanna spawn off four threads if I wanna do that real fast. With a persistent data structure you can pass a reference to four threads to the same list and not worry about it. If you do that in a Ruby array and all the threads started mutating the ray you would have utter chaos, it'd be pandemonium. Persistent lists are thread safe. Interestingly, they're also lazy. So we have laziness in Ruby. So take a look at this algorithm. So say I wanna find the first 10,000 primes. So here's some simple Ruby that finds the first 10,000 primes. Does anyone see a problem with this? Yeah, I'm finding all the primes between one and a billion aren't I? That's a little efficient, I don't need all those. I'm not sure if that would ever end. It might take a very, very long time just so I can take 10,000 at the end. So what could I do? I could use the old procedural version where I get an empty array and do a loop and keep a counter of the number of things in the loop, right? That's not very elegant. Ruby gave us this dot lazy keyword, which is brilliant. All I change here. The only difference here is that dot lazy right up there. This means, look at what I got out instead of a bunch of prime numbers. The answer I got there is like this, kind of stacked up lazy data structure waiting to be executed. So you can actually do this. By doing a lazy select, then you do dot take. You have a lazy calculation waiting to be fired off. So when you like puts it or like call dot it, like 2a on it, it actually fires off and gives you the answer. Hamster data structures also do this. Hamster structures work the same way. They are lazy by default. So in addition to being a mutable persistent, they're also lazy, and that's kind of clever because the way my algorithm works, basically I'm allowed to like kind of stack up this recursive tree search algorithm, a flat map, a cat, and I only fire it off at the very end. In fact, what I actually do is I have this lazy sequence and I actually call that select like basically where is a winner, right? And then it still hasn't fired off. Then I basically call it dot first and it actually traverses through and finds it. It's very, very cool. So hamster lists are lazy. They're awesome. And we get a few more really neat things in the hamster library. We have the list class, which I showed you. There's a hash which acts like a Ruby hash but persistent. We get a set, a sorted set. A deck is a stack or a queue, but we actually get this vector. And the vector's very interesting. I mentioned there are a lot of limitations in the list class, right? We can't do random access. We can only traverse one direction. Those are some limitations, right? And sometimes we need that. Sometimes we need random access. We need to say like, what's the third element in this list? Hamster gives us this. So here I'm setting V1 is zero through eight. And V2, here I'm setting the element five to beef. So I'm getting back zero, one, two, three, four, beef, six, seven, eight. And you can see that V1 is untouched, all right? So this is persistent. How did they do this? Think about this for a second. That's actually a pretty tough thing to implement. This one I scratched my head about for a while and I was very, very pleased when I saw the answer to this because it's quite clever. This is this fancy data structure called a persistent bit-practitioned try. That's a mouthful, I know that. But notice a try, first of all, a try is a tree where all the values are in the leaves. So you can see that all the numbers that I actually care about are all the leaf nodes. That's a try. All the middle nodes are actually empty. They're just placeholders in the 10 at the top. That is just a cache of the length of the list. So what happens when I replace five with beef in this case? It actually changes things like this. So the blue here is the new vector that I've created. The orange represents the old one. You can see it builds up the tree, but the blue one actually shares some of the orange nodes with the original, right? That's pretty slick. So it has to do absolutely the bare minimum. We have to make a few of those middle tree nodes. We have to make a new node at the bottom containing four and beef. But the rest of the stuff, the six, seven, the one, two, three, four, five, those are all shared in memory. It shares the same instance. So that's how I'm able to change something right in the middle of the vector and also not have to clone the whole dang thing. A pending has a similar kind of thing. So I have zero, one, two, three, four. I tack a five on the end. I only have to rebuild just a tiny little bit part of the tree so it's nice and fast. Popping, so here I have one, two, three, four, five, six, seven, eight, A, B, and C. I remove C, and you can see that the blue part, the blue vector, shares most of the memory with the original one. Very, very clever algorithm, very, very clever implementation of a persistent vector. I thought that was real neat. Shout out to Jean-Nicholas Lorange. I took this graphics from his excellent, excellent article, which if you want to read it, I highly recommend it. It actually explains Closure's implementation of this, but the Hamster version is the same. So that's pretty, pretty neat. But what do we do with it, right? So what? Why is this useful? Turns out there's some really, really cool applications for this. So what I did is I made like a little demo application. This is Connect4. Do you all remember this game? When I was a kid, actually the colors were different. It was a yellow board with red pieces and red and black pieces, I think. I don't know what point Mattel decided to change the colors of this, but so what I did is I represented my Connect4 board as a matrix. So let me show you Connect4 real fast. It's a pretty simple program with a pretty profound difference than what you might be used to. So let's see, Ruby Connect4, Connect4 Ruby, boom. All right, so I'm gonna make this nice and big. Also to the right a little bit, yeah. All right, so I actually made this, this is a pretty simple little demonstration. I built it with emojis, just representing a Connect4 board, but so I'm gonna start adding pieces to this board. So column one, of course I'm a programmer, so I use zero based indexes, right? But I started adding pieces to this board and I'm playing my little Connect4 game. All right, pretty simple. Nothing really special there. Why is this interesting? All right, so I represent my board with a hamster matrix. Hamster matrix is a gem I made, brand new gem just for RubyConf. It's a 2D matrix. What if I change something? An original reference is untouched. It's a persistent matrix. It's pretty simple actually, it's just built on top of nested hamster vectors. But I made my little hamster matrix gem. My board is a matrix. Oh, I'm not cloning because we don't want clones in the matrix, right? No, that'd be bad, right? I know why I was too easy, sorry. Okay, but this command line program actually follows a pretty interesting architecture. Basically, I have a state manager. The state manager holds my state, which is my matrix. And when I press a key, I take that state and I send it through my game logic. The game logic does things like checks, like, okay, is this a legal move, right? You know, I drop it in this column. You know, what road does it actually land on? You know, so where does that slot in? You know, did one of my players win the game? That's my game logic. And I get back a new state. Because it's persistent, we know for a fact that the old state, the original state is still in memory somewhere, don't we? That's kind of neat because now I can store all of the game states that I've ever been through here in memory have a reference to them all. So I have my entire history of game states here in memory in my state manager. Why is that cool? Because I can travel through time. Yes, very, very, very cool. And my state manager, that's really neat because at any point in this, I can go back. Wonk, wonk, dunk, dunk, dunk, dunk. So I can rewind my program. There are a lot of ways to implement this, but can you think of anyone that's more clever, that's more eloquent? Because I pretty much got that for free, didn't I? By using a persistent data structure, the whole memory mapping, keeping all that state memory, it's pretty much done for me. All I have to do is keep a list of references. That's real clever. I don't have to clone anything. I don't have any inefficient cloning, do I? So it's a real, really easy way to achieve that. And it's also real efficient. Of course, because my persistent data structure is in my state manager, all I'm really doing is storing diffs here, right? Since I'm not cloning anything, I'm storing the absolute bare minimum to represent the difference between one state and the next. So it's crazy efficient. It hardly takes up any room. It's very, very, very fast. There is a little caveat to this, right, to make this work. My game logic cannot have a side effect. A side effect. It has to be made of pure functions. What is a side effect? All right, so here's a little Ruby class with a person. I've got this method, steal cookies, and you can see that I'm incrementing pent-up aggression, right? I'm changing something. Pent-up aggression is being mutated, being changed. That's a side effect. If I do that in my game logic, and if I have a bunch of objects around, and I'm changing instance variables in my object, those are not reflected in the state, are they? I didn't capture them in my state manager. When I go rewind, they won't be rewound. So there's no way to rewind if I have random mutations in state variables in different places. Which means I have to be, I have to basically write my game logic in sort of a functional style, which is data in, data out. I pass arguments into a function. I operate on that. I get the answer back. And I, my virtuous circle there, I send the answer all the way back up to my state manager. That allows me to time travel. It's very, very clever. So where is this all going, right? Turns out this, a lot of the industry is moving that way. So this is more than just a novelty, right? Connect for a game. Okay, that's kind of novel. It's kind of neat. But this is a lot more than just a demo. For one thing, our front-end stack where I work at Procore is entirely based on these principles. In JavaScript, of course. This is our front-end stack. We're using React.js, which is very popular, along with a library called Redux, which is a implementation of relay. And we're following this virtuous circle. So a key press hits what's called a store, what's called a redux store, which has the data stored in immutable vectors using Facebook's Excel and immutable.js library. I pass the state into a redux reducer, which gives us a new state. And then we send it down to React and we send it down to the DOM. We can actually rewind and fast forward our front-end at Procore, which is really, really neat, really, really clever. So that's nothing to joke about because we also have a list of hot loading, which means that we can actually pull up a form, start pressing buttons, doing JavaScript-y things, to change the page, go back, rewind it, go back to make a code change, get hot reload, and then actually start clicking around things again. That's a pretty powerful debugging tool. Interestingly, this stuff is moving to the server side, too. So this is Rich Hickey. I think he's the most interesting computer scientist living today. He invented the closure programming language. He invented that persistent data structure that I showed you, that vector that he invented that, which is really, really cool. He also invented a database called Datomic. Datomic is basically a persistent data structure on disk. It's basically a database that never forgets, that uses these exact principles, like basically a vector or a link list written to disk. And he sets up very interesting things. He's got a talk. It's called The Value of Values. It's from 2012. It's a modern classic. I highly recommend you watch it. There's a reason we do place-oriented programming. And here's what he means by that. Place-oriented programming. Think of a database row. Think of a database row when you update a field. Last logged in at, right? You update that. When you go back and retrieve that, you're looking for a place. It's a place in memory. It's a place and you're changing something. I always know the last updated date is at the certain place in the certain row in memory. In an object, I've got an instance variable. That is a place, and it's something that I change. But if I need to know the value that I'm looking for, I go back and I find it in its place. In other words, we have places any time we mutate something, any time we change something. He says, basically, the only reason we ever do that is because in the early days of programming, memory was expensive, disks were expensive, rams were expensive, we had to update, we had to delete because we couldn't afford to have enough memory for everything we needed to do. Those limitations are gone. Ram is cheap, disk is cheap, which basically means why are we still doing that? Why are we still changing anything? Why don't we keep the history of everything we've ever done? Okay, that has some ominous 1984 tones to it. That's not really what I mean. I mean, the useful type of this. But that's a good question, and his database Datomic actually follows those principles. Can you imagine for a second if you built a Rails app on top of Datomic so that you could actually pull it up, start doing things, filling out forms, doing CRUD operations, then you could actually be wind it to a fixed point in time, then fast forward again if you could give your users the power of actually like, all right, here's where you are now, here's where your budget or your accounts are, or your email is now. Basically without any, with very simple implementation details, you could give it the power of your users to rewind and go back in time. See, here's where you were. This is what it used to look like. You have complete all of your history of everything you've done on disk. That'll be pretty, pretty amazing. Can you imagine how great of a debugging tool that is? If you can pull up a Rails app, do CRUD operations, oh man, I messed something up, there's a bug, rewind it, fix the bug, play it back forward again. Pretty slick stuff. And the thing I'm most excited about is this new language, it's called Elm. Has anyone heard of this? Yeah, this is good stuff, isn't it? Elm is a purely functional language and it takes away a lot of stuff. It has no classes. It has no nil. It has no loops. It has no variables. And it's strongly tied. If it compiles, you're guaranteed to not have runtime errors. That sounds crazy. It's like, it's literally like a quarter of Ruby. Like, it has like a quarter of mini language features. It's the most beautiful example I've ever seen is making something great by taking stuff away. And I'm gonna show you my little demo here. It was also very, very fast. And so this is my Kinect 4 game in Elm. You'll notice this is running in Chrome. It compiles the JavaScript. So this is a language for front-end games. Interestingly, it enforces that pattern. That pattern of state managers going in a loop with pure functions, it enforces that. It is impossible to write a program in Elm that does not follow that architecture. Because it's impossible, every single program you write in Elm is out of the box, time-travelable. It's, which is so nifty that they built it right into the language. I can actually, right here, go yank, add a query string, debug. The time-travel debugger that's built into Elm pops up on the right side. I can start to play Kinect 4. Well, I didn't like that move. I can pause the program. I can start to, sometimes there's a little lag. I can start to go back in time. I can start to go back in history. So because the language enforces it, every single program in Elm is automatically time-travelable. That's pretty neat. So just a few resources before I hand this off. All my code that I made is online, the block source solver, my new hamster matrix gem, my Ruby version of Kinect 4, my Elm version of Kinect 4. I highly recommend this course, principles of object-oriented design and Scala. Some great reading if you want to read more about persistent vectors. There's the article that I took those graphics from is right there. If you want to read about Git, being a purely persistent data structure, that's in there. The value of value is by Rich Hickey, modern classic. Highly suggest that if you want to learn more about this really, really cool stuff. I am Brad Urani. I'm addicted to Twitter. Follow me. I'll follow you back. Connect with me on LinkedIn if you want. I've got a blog. I don't update it very much. And usually when I do, it's me complaining about what I don't like about ActiveRecord. So, I work at Procore in Santa Barbara, California, aka Paradise on Earth. We're one of the, we make construction management software. We're one of the diamond sponsors at this conference. We've got a booth at the end of the hallway staffed with great people. All the people we brought are engineers, not recruiters. Highly recommend you check it out if you're interested. It's a great place to work. We're growing fast, hiring like crazy. We're also throwing the party tonight. It's at eight o'clock at Howell on the Moon on the Riverwalk. It's gonna have dueling piano bar, a band, free drinks for two hours. And raffling off some cool stuff. I think an iPad Pro and things like that. And that's my talk. I will tweet this and make sure that it gets out on the internet. Follow me on Twitter and I will tweet this and you can find my resources. I'll tweet the slides and the demo. All the stuff some might get, by the way. Thank you very much. Thank you very much.