 My name is Bruce, I'm the author of Seven Languages in Seven Weeks, also a book called Programming Phoenix, and I think Noresh was going to say I'm also the author of a book called Bitter Java, but you don't want to remember that one. So in this session we're going to talk about a framework called Phoenix LiveView. So let's go ahead and write some code. How many of you have heard of a game called Conway's Game of Life? Okay, so about half of you. So it's a game that's played on a grid. There are cells on the grid, they're going to be live or dead. Live will have a dot like this one, and dead will be empty. And each cell has neighbors and we count the corners so that this is a pattern in the game of life where this cell has two neighbors and this cell, this orange cell has three neighbors. Got it? So the rules are that in the next generation, if any cell has exactly three neighbors, it will come to life. If it has fewer than two neighbors, it will die of loneliness. Or if it has more than four neighbors, it will die of overcrowding. Others will stay the same. So your quiz, what are the four rules? What's the first one? If something has three neighbors, it creates a new life, right? What happens if there are fewer than two cells? It dies more than three cells? It dies. Otherwise, it stays the same, right? So we're going to code the game of life in a framework called Phoenix. And I want to tell you a little bit about Phoenix LiveView. I've downloaded the Phoenix LiveView example and we'll use that as a template so we won't have to do the boilerplate setup. And just so you can see, exactly what I've changed so far, I've only changed these two files. I fetched my assets, right? So Phoenix LiveView uses JavaScript under the hood but doesn't require you to write any custom JavaScript yourself. And then I've also changed one security code in this file called endpoint just to make it compile and run and everything else is the same. So Mix is the Phoenix catch all command for the development environment. We're actually going to start a server with this application. And I want to show you a couple of examples in LiveView. So I'm going to navigate to local host 4000. I'm going to zoom in a little bit. So these are a couple of LiveView examples. Let's talk about the individual pieces. So here's an example and basically everything that you're seeing, all the code is done on the server side. There's no client-side custom JavaScript at all, okay? So that, you know, all of this is running on the server side. It has Erlang behind it so that if something happens so that it would crash, so we basically built in a custom crash, if the temperature gets higher than 75, what happens is OTP kicks in and restarts the process in the last known good state, which was the starting state. So I want to point out that this is fairly performant, right? So this is a game. Remember all of this is server side. So the performance is fairly good. Any mistakes are my own. You know I'm not very good at snake. And actually you can see that we're actually fast enough to power things like animations. Again, everything, every change is computed on the server side, okay? So we're going to, we're going to write a program in Conway's Game of Life. So we need to start this on the elixir side. That's actually where we're going to spend most of our time, creating the board and the rules of the game and so forth. Okay, so we're in live view here. How's the font in the back? I know the left-hand side, I can't change that, but how's the font in the right-hand side, this part right here? Okay, in the back? Better? Okay, so we're going to create a new file. So there are two directories to pay attention to here. One is demo and that's where the back-end files are and one is demo web and that's where the front-end files are that are related to controllers and so forth. That's where we'll put our live view files. Let's go ahead and start with the back-end file. Let's create a file called life, demo.live, okay. So this is going to be a life board and life is played on a grid, right? So a grid has a height and a width and let's have a default height of three and a default width also of three and let's talk a little bit about the data structure that we're going to use to save the grid itself. Now in a functional program, so in many object-oriented style languages I might use a matrix or a list of lists, right? But in functional programs those data structures are immutable so sometimes it's impractical to use a data structure that's nested. So often when I'm coding functionally I want to keep my data structures as flat as possible and that's what I'll do here. So I'll save the board or we'll call it a grid and I'll save it as an empty map. In Elixir the braces are reserved for tuples so we have a percent on the front end of map constructs. So now I have a, so I'm going to start mix in the context of my application. Save that. Now I have this life and it has a struct and that struct has a grid which is my map and has a default height and width and so I could create a new life and get the height and width. That much is working fine. I never did like this syntax so let's create a prettier name for that, so let's actually just create a function to do the same thing and I'm going to create a new struct and the attribute is going to be key value pairs, a list of key value pairs. This means set the default value if none is provided. So in this case we're going to say this is the struct, I could say life but instead of doing this, if I change the name of that I want Elixir to track me. So there's a module attribute for that called module and we'll use this instead. So basically this is the structure that we define right here, right? Or actually I can say module dot, let's see how that works, okay? Now I can say, so I'm not using, I could actually use those attributes, okay? So now I can actually take, let's say life new and I could say, I could override one of the default attributes like that. Okay, so so good so far. Let's say that the on state is, I don't know, maybe we'll have it, or let's say a live state we're going to represent, let's make an emoji. Maybe something like this one, or this one, yeah, one of those will work. Let's put quotes around that and for the live one we'll pick that one, and for the dead one we'll pick that one, okay? Okay, so now we have live and dead cells. So let's fetch something from the board. So we're going to take a board which is something of this structure right here and it's going to take an x and a y and so actually for this grid let's make the keys be tuples, be two tuples, right? So I could say map dot get board dot grid and I want to get the tuple x, y and then if there's nothing in it I can provide a default value of dead, right? So now let's see, okay, live dot new and I can pipe that into, use that as the input to live dot fetch and so the board is my first argument and that's created right here. So let's get whatever is at one, one, right there, okay. So we're working fine, we got a dead cell, that's exactly what we want to happen, okay? Okay, so now we need to start thinking about printing out a representation of the board. Which way should we go? Let's create a new random board so we could have some data to work with, new random and let's say that this takes a height, okay. And so this is going to, okay, so how are we going to do this? This is going to take a four comprehension so let's say for x taken from one to width and for y taken, taken from one and we're going to execute this code in the middle and then we're going to start with a list that has live and dead and we're going to shuffle that to put it in a random order and take the first one, okay. And so what I need to do, that's the cell, right? So now I have a cell and now I need to build a key value pair. So the key is what? It's the x, y coordinate, right? And the value is the cell. So now this should give me, this should give me at least the grid for my life game. And now I need to return the structure, take this guy and I want the height to be, the width is going to be width and the grid is going to be grid, okay. So I think that we have something that will work here. So let's try to do a, try to see if we can, yeah, let's say line number 12, oh thank you. Types would have saved me there, wouldn't they? Thank you. Okay, what do we got? Syntax error before, we're going to have a few of these so bear with me. So line 21, oh this is a two tuple, right? This isn't a, and we probably want to put that into, yeah, we want this to go into a map, don't we? Okay, so now we should be able to say life and we want a five by five grid, right? Looks like it's working perfectly. Okay, now we're getting pretty close to having the basic board and we're getting pretty close to time to create the next generation. For right now, let's create a pretty version of the board. Okay, and so basically for this, we're probably going to, we're probably going to want to convert each row to a string. So I'll say four y taken from to board dot, and we want to basically generate a row and then we want the board and then the row, right? And then the, okay, and now to generate the row, let's say we have, we have the, we basically have, we need to iterate across the board across the, across horizontally now. So now we can say for, and this is actually the y coordinate, right? So I could say for x taken from one to board and then I want to basically return the character of the board, which is fetch, right? So now I'm at board or fetch x and y, right? That's going to give me a list of characters and I'm going to join that on the empty list, right? And now I can join this, let's see what we have now and what are we trying to do? Okay, and then we want to pipe this much. We're going to pipe that to life dot, okay, now we're getting pretty close, right? So that's, that prints a generation. So it's about time to start working on the rules of the game. It's going to be really close, folks. So it's about time to start working on the rules of the game, so let's do that now. So what's the first rule? Okay, and we have a neighbor count and we have a cell. N is less than two. What happens to these? And similarly, when N is greater than three, they die, right? And we don't really care about the cell in these cases, right? So N and the, when N is three, for N and cell. So those are the rules of the game. Now I have to create the next generation, right? So the next generation of a board, so for X taken from, oh, we need to count neighbors, don't we? The neighbors of board at X and Y, okay, so what's the best way to do this? Let's go low tech, just to be safe. So I have, basically, I have X and Y, I have three of those, and there's going to be X minus one to X plus one, like that, right? I have three of those, and I want to do, these are all Y minus one, right? These are all Y plus one, and this, we don't want, right? Did I get that right? Yeah, we have a closing comma to delete, right? And we want to pipe this to, I'm going to map over that, dot enum, dot map. I'm going to map over that, and I'm going to call fetch, and I'm going to fetch from the board, X, and I'm going to call, and here, I'm going to call, I can't use X and Y, can I? Oh, I'll call this X, X, and Y, Y, right? No, that's right. I can't use X and Y here. X, X, and Y, Y, right? So I'm going to fetch, X, X, is that it? So I'll fetch them, and now I need to filter by life, right? Where that guy is equal to, is that it? So let's try to compile this much to see where we are. It'll be a miracle that neighbors is unused, rule is unused, and board is unused in 62. So I bet we're okay in all those, yep, we're okay. We're about to use them right now. Okay, so we've got about 15 minutes. I think that'll be enough. Let's see, okay, so the next generation is for X taken from, for X taken from, board, dot, width, and that's, I actually want that to be a range, right? And Y taken from, to board, board, dot, height. And we want to collect tuples now. We want to collect key value pairs, right? So this is X, Y, and now we need to basically process, get the next cell, right? So this is, so this is what? We want to get in, we want to pass, we want to get the rule. So that is the neighbor count, right? Which is neighbors, and the cell, and the cell, which is fetch, okay. I don't know if I'm feeling it, feeling kind of rushed today. Okay, what's happening here? 64, anybody see it? 63, yep, thank you. Okay, did I save? What do we see now? On 64, missing terminator, okay. I said there would be plenty of this, and there are. Oh yeah, yeah, this is, how about that? This is a tuple, right? That's a tuple instead, and that's rule. We need to close that. That was gonna fail doubly, wasn't it? Okay, how are you feeling? We're gonna get it? So let's see if we can take our next generation board from, so our board is equal to life, new, random, for five, five, right? And I wanna pipe that to life, next, next generation. That looks pretty good. Ah, I'm feeling it. Okay, so now what we need to do is create a route. We're almost done, believe it or not. So now we're going to create a route for this thing. So I'm going to the router. Let's just take the pattern. So basically what's gonna happen is any new web request is going to come in on the path, and if I mark the route with life, it's not going to go to a typical Phoenix renderer, it's going to go to a live renderer, right? And what that means is it's going to basically start a loop, right? And that loop is essentially an OTP loop. So let's take one of these, make it look like this. So if I go to life, I want to go to life, live. It's almost a tongue twister, right? Okay, and we need to create that file, right? So I'm going to the live template here. Let's take a look at one of these things to see what it looks like. Okay, so the anatomy of a Phoenix live view is that when something comes in on the route, it fires the mount first. And all this does is stuff the key value pair, right? So this is the data model for your application. And then that data model gets shoved through a renderer, right? So, and then if you want to update it, you update it through OTP events, right? And these come in as handle info. If you're sending it from your application or handle event, if it's coming in from a JavaScript, WebClick or something like that, right? In this case, we get this tick message and that's coming in from this timer that gets sent at the intervals of a second. So this looks like something we can use. So let's go ahead and grab this and we'll use this as the foundation for our clock life, right? So new file, life, live.ex, okay, and this is life. Okay, so if we go here, we should see the same. If we go to, I should see the clock and this clock is actually being created in our live view, right? So here it's straight HTML except this substitution and this substitution, since this is a live view, live template, this substitution is going to get updated every time that there's an event. And live view will actually process the bare minimum that needs to happen to make this page render. Okay, so let's take over this for our own. We're going to maintain this timer call because I think that we'll need it. And in the socket, we're going to say, we're gonna put hello world in there instead. I don't need the put date anymore. And we're going to, we're gonna live render so we're going to say at hello, here we'll put, let's put that in there. Okay, put date, so I've called that somewhere as oh, right here, handle info tick. So let's just return the socket here and just do nothing when we tick. Okay, so now we should see. So basically what I've done, so remember when something comes into the socket, it calls mount first, right? And in mount, all this is is a key value pair and we've put the value of world into the variable, into the key value set of hello, right? Into the key of hello. And then we've said plug in hello right here, right? And now we get to tie everything together, right? Because we have a clock, the clock is ticking every second. We have a board and so now all we have to do is mount this board and hit the next generation, right? So let's go ahead and mount the board first. Life is equal to, what is it? It's life.new random. Let's do something like 50 comma 50 and then we're going to assign life and that is going to be life. But up here, we need to use, we need to render that in safe HTML and we need to render, we need to do something special to render this. This is life. What, two string? And our board is called life. So let's see what's happening here. Okay, so we haven't grabbed our module yet. So we're on the right track. Now all we need to do is alias demo.life. Ah, that looks good. So what's next? Any guesses? Next generation, where does it go? In the tick, right? So basically I could say, I can assign socket and what am I assigning? Life, but we want, this is the next generation, right? This is getting a little ugly so let's pretty that up a little. And we want to call the next generation here, right? Keyword argument must be followed by space after life. Life, oh, here we go. A row comma. Okay, let's see what's breaking. Oh, okay, we're not socket.so, oh, it's in life. Yeah, see it's working. No, what's actually happening is we have a crashing loop, right? And so we have to debug this crash. Okay, so what's happening? Life.nextGeneration, so we're taking a board. We're basically passing the board in. Let's do this. Life equals, see if we can get a better error message. Okay, here we go. We're getting an RE, okay, so we're doing an Erling apply. Oh, we're trying to grab height from this thing. So it's the next generation returning the board. Let's take a look and see. Okay, I bet I know the problem. So next generation, what's this actually returning? This is returning tuples, right? So this is the grid. Now we got it. And I wanna return the board, but I wanna update the grid. I wanna set the grid to grid, like that. This is killing me. Okay, now I get an argument. Okay, I'm just gonna bounce the server. I think that this should be working. It is! All right. Right on the minute, it's 10.45. Okay, so in conclusion, what we've actually shown you is that we spent about 80% of our time on the business model and about 20% of our time doing the interactive web UI, which is almost exactly backwards of a typical application. So I have about 100,000 lines of code invested in a new project. And I can tell you that putting yourself in the headspace on the server changes everything. And that's it. Can we take two questions? So two questions that may go together. One, what's the communication protocol between the server and the client? Yes. And two, are you using some kind of a virtual DOM to reduce the Diff? Yeah, so the virtual DOM is Morph DOM. It's a JavaScript framework. But basically, I don't touch any of that. Like you saw, all of that is managed by the framework, which it should be, right? And the nice thing about that is that when you saw the substitution, the substitution does an elixir-based reduction to see if the variable it has changed in memory, right? And then if it's changed, it actually sends down the HTML associated with that variable so that you get the minimum set sent down, right? So the communication protocol, it's over HTTP sockets. It's over Phoenix channels, basically. Is that like a web socket? That's a web socket. That's a web socket, yeah. Thanks. Sure. Another question. So LiveView is very nice. Like, where do you see, like this is disadvantage or where we can't use LiveView? Where can you use LiveView? Yeah, so I tried to use in dashboard and a couple of things, but do you see like some area where we can't use? So I am seeing four things that are dramatically different for me, right? So the first thing is that one of the problems that I'm trying to solve is building an interactive lesson, right? And so I can actually do a lot of my modeling in OTP and I can stay away from database persistence for a very long time and actually work on my models and work on the LiveViews like this for gosh, weeks before I ever touch a database and before I ever write a single migration. That makes a huge amount of difference, right? So the second thing is that there are all these tiny JavaScript things that we've taken for granted that must be done in JavaScript. Like when you expand and contract little pieces of a page, that is so natural to do on the server side. I just keep a map of the expanded and contracted sections and then show them or don't show them based on their state, right? So the third thing is that it is possible to build a better performing application using JavaScript, but you have to be so very careful, right? So if you have a typical team and typical application development processes, it's sometimes better to let the computer do the work and compute the minimum set, right? So I know that everybody is looking at this demo and said saying, well, that's probably taking a long time to do the back and forth, right? But think about how much work we throw away for the typical web environment, right? So look at a talk that Chris McCord does at Elixir Conf this year and he has something called the Atomic Trash Can. He has all these things that are computed and thrown away for every single web request and all that is just kind of, it's maintained in the single session so it does much less work. And the fourth thing that I think is coming is Elixir is also a very good environment for the internet of things. And so you can imagine that right now we're seeing this done with HTML. Well, what if we put this behind something like SVG, right? The principles are exactly the same, right? That becomes a really cool way of modeling the world. What it doesn't do well is applications that have to run online or I mean have to run offline. And also if there's a lot of drag and drop, those types of things, a lot of mouse tracking that you need to do on the user interface. It doesn't do those well yet but I imagine that those types of applications are coming.