 All right, so as Stuart already mentioned, my name is Bordale. This is my Twitter handle. You know what to do. And my talk was supposed to be called ClotureScript All the Way Down. But somebody earlier complained about overuse of memes at this conference. So I decided to instead call it ClotureScript All the Things. Right, and the concept here is I'm going to basically be writing an application in ClotureScript, a web application, with a server and a client side, both in ClotureScript. And this is all going to happen live on stage. And the rules of live coding is that you are going to help me because I'm probably going to run into typos and stupid mistakes. And I'm assuming the people in this room are the most qualified people in the world to help me out. So if I fail, that is your failure, right? OK, let's get started. I'm going to give you a quick overview. I have some files prepared already. I'm not going to write everything. Like the CSS, you don't want to watch me do that. Here's the project file. Yeah, Dan Freeman asked me if it's called computer science all the way down because that's what it says on the program. But it's not. And I am basically using a version of Hiccup. There are lots of Hiccup ports to ClotureScript, but all of them produce DOM trees. And I need it on the server side when there's no DOM. So I was lucky to find one portal Hiccup to ClotureScript which actually produces strings. And that's what I'm going to be using. It's called Hiccups. Because you know, CNJ is the NJ. It's the NJS. Hiccups, Hiccups. Makes sense. And of course, I noticed Conor Boski's talk the race ago and decided to rewrite the code to use this one framework or at least a little bit of it. Further on, this is Node. This is running on Node.js. So I've used some Node technologies to power the backend and express the traditional Node.1 framework. A MongoDB driver. I'm going to be using Mongo for the database. And Socket.io to do communication between client and server. So let us go on to the server code. I'm just going to enlarge here. So first of all, ClotureScript data types are not compatible with JavaScript data types. And while there is a JS to CNJ function in the ClotureScript core library, there's a conspicuous absence on the opposite. So as most people have, I had to reimplement this. So I'm including Hiccups. And I've written a very lightweight Mongo wrapper for ClotureScript. So let's just get started. Let's just get a server going. First of all, we start by requiring some Node stuff, like the HTTP package, and express, and Socket.io. Right. Now, Node in ClotureScript requires this strange location to define the entry points. So we have a function main, which takes args, which we just ignore. And let's just connect to the database right away. Yeah. So the application I'm going to be writing is a to-do list. I didn't know Rich was going to be doing the same thing. And basically, I'm going to make a checklist of a program language theory concept that I want to learn. So we're connecting to the database PLT, and it takes a callback. We're already in callback, because this is basically JavaScript interrupt. So this should connect to the database, and then call on a function called server. Bring the database. Right. So I am going to use what I like to call MoxStateMonat, just an atom to maintain my connection to the database. So let's put the collection. So this basically stores a reference to a table in the Mongo database called concepts. And then let's create the actual web server. We do that by calling express, and we are going to wrap the express app in an HTTP server. And let's see where we are. A threading macro. We need to configure this app to display our static files. And because static is a reserve keyword in JavaScript and the closure compiler refuses to let us have closure script symbols called static, we're going to do some trickery. We're going to fetch the static property using a string out of express, and then call it with our static file folder to define where we keep our files. And then we just define our main page. And now the function getMain takes a request and a response. Should just basically write something back on the response. So standard hello, Alfa, so long. This should almost be running now. We need to turn it to start listening. So listen. So about 1337, that sounds about right. So closure script. Oh, great. What am I doing wrong? What does that even mean? Yeah, what's wrong with that? Oh, sorry. Sorry. Sorry. It's a function call. See? I turned it to watch out for this. And closure script has to compile, of course. And this brings me back to the Hansian days of C++, I must say. There we go. So I'm going to run because Katnip, my editor, hasn't properly integrated with actually running node applications. So I'm just going to use a transcript for that. That's just going to watch my main.js file that the closure script compiles to. And so wish you'll be able to actually see something happening. Hello, world, that was awesome. Right, but let's get fancy page template. Let's just make a function that actually renders an HTML. And call that. So first of all, let's just check that that's working. By wrapping the hello sailor into a header tag. Compiling, compiling. The server code is especially slow compiling because I need to concatenate it. So I'm using simple optimizations. Wow. What happened there? Oh, yeah, of course. We need to, of course, call hiccup HTML. And then just let's just get that. Re-compile, yeah. I'm just going to carry on while we wait for that. So here we go. That's HTML. But let's get webfui involved. I made some webfui boilerplate. I'm just going to comment our socket IO until I need it. And re-compile that. And then get over to the client side. Now, what we should be getting here soon is, yeah, we see it's changed color, so the CSS must be working. And it should be loading and running our closest grant. So just quick check to see if that works. This one should compile in a faster, yeah. So let's get started on this side. Let's just get webfui up and running. We define a DOM, which is an atom, which starts up being HTML. We define some state, which is another atom, which is just going to be an empty list. I'm going to put my documents in here as we go. My two-day list entries. And we call def DOM to make webfui aware of what the DOM to be. And we make a function to update the DOM and a function to render like that. Just wrapping everything in a div because it didn't seem to work otherwise. And then, right. Now, I'm only using a very small part of webfui. So I have to automate the process. My time to get it started. So I didn't mention the use statements on top here. I'm reusing CLJJS because you knew that everywhere. I've written a very, very lightweight DOM library, which for one thing has a DOM ready function, which takes a call back, which is called when the DOM has loaded. And that's webfui. So when we have a ready DOM, we should just call update DOM. This should hopefully give us a random webfui. We're guessing that. Just something isn't quite right. Let's just add something. That looks better. OK. So now, let's just add some states for now. Go to, and we're going to have a property called DOM, which is a boolean. Let's just try and render that. Let's make a function, which takes a document of the kind that we have on top, and returns a list item. Give it a class. If it's done, then it should be done. Otherwise, it should be open. See? Wait. There. And a span, class check, with a data ID of item ID. This is added by MongoDB later. We're just going to use it to refer back to this. And the content, if done, done. Then we have a nice little UTF glyph for the checkbox, and here's the glyph for the box without the check. And finally, a span, which contains the actual text. Like so. Because nothing happens, because I do have to actually have an IUN, and then quite simply, we just, remember my state atom, where did we go? There we are. We just basically mapped the contents of the state atom over the concept item function. And that should give us a pre-populated list we'll go to. So nothing fancy so far, but let's get the database involved, and let's hook up Socket.io and see if we can get something interesting going. First of all, I'm just going to add an input field. Text. And then we need to connect the Socket. Dev Socket. This doesn't need to be an atom. This is just JavaScript. Connect js-io. Back to the server on NoClosed. Yeah, that should be good so far. Back to the server side. You notice it went blank now. That's because it's trying to use Socket.io at the top of the script, and it's not loading. So here we go. But it's still not going to work, because we need to actually initialize Socket.io. So just go fullscreen for a bit. Let's just initialize that. We'll turn Socket.io to install on our HTTP server object. And there I get to use my .minus notation. No, that is correct, in fact. Because we are going to fetch io.socket. This would worry in io.sockets.on. So this is right. So we're calling the on method on the connection event. So every time a Web Socket connects to the server now, this function is going to be called with a Socket object. And what we want to do is we are going to send the contents of the database to a Socket when it connects. So let's make a send docs function that does that. I seem to be coming down with Phil Hegelberg's disease. So there may be some coughing. So we're going to send docs, take the Socket. And we're going to call Mongo. Mongo, find all. The print screen key is right next to my old key on the key, but it's so stupid. So it takes the collection we want to run the query on. It takes a query object. And it's just going to be an empty map because we want everything. And then it takes a callback, which should just be, it takes the result. And we just call emit on the Socket, which sends an event to the client. It's going to be called docs. And we need to convert that object to JavaScript before we try sending it. And that should send this event to the client with all the data we need to render. So back to the client and let's do that. So we have a Socket object already here. So we should be able to just add a listener. Docs and a callback on new docs. And we are going to create a function for that. Convert them back. Yeah, for JavaScript interrupt. Not a bother at all. And then put the new state in our state atom. And tell Webfully to re-render itself. And that should actually be it. This should. Unless I'm very much mistaken, I should now render the contents of the database. Notice our go-to just vanishes. Let's run that again. Let's pay attention to the go-to. It's there for a split second. So what happens is that it first renders the contents on the state atom at initialization time. And then it gets an event from the server, which contains nothing. So this is all I can just find. Let's just take that out. So we want to start populating the database. So let's get back to that input field. Now, another function I have in my little DOM library is a function called watch, which listens to DOM events. And it takes the event name. It takes a CSS selector. I had to hack this a bit because at initialization time, the DOM is empty. Webfully has yet to render it. And it runs in asynchronously, apparently. So I can't just attach events to DOM elements that already exist. So I'm attaching them to the document and then using CSS selector to filter. But like jQuery does. Yeah, let's get back to what we're doing. So, no, sorry, on new, on submit. It's a good name for it. So I want a function called on submit to be called when the form is submitted. It takes an event. And since this is a form submit, we want to make sure that it doesn't just drill out the page. So this is just standard DOM. And then we need to find the input element. Q is another DOM function, which just, as you probably can guess, finds a DOM element using a selector. Now, the value of that input field shouldn't be stalled. The Nash is intentional once again. Right, and I want this to work in such a way that when I enter something in the input field and press return, there's something Webfully doesn't support, by the way. I hope that's coming, which is the reason, by the way, why I'm not using AutoWebfully. So when I press return, I want the input field to be cleared and the previous contents to be sent to the server. So we set the value to empty string. And we emit an event like that with a previous value. So the server doesn't implement this yet, but I'm just going to test that and it looks right. So I enter and it vanishes and presumably the event is triggered, but nothing on the server is sponsored yet. Let's do that. So whenever a WebSocket is instantiated on the server, we want to listen to events as well. It works the same way as on the client. We have a new event, isn't that why I call it? So on NewDoc socket, sorry, sorry, sorry. We need to include the socket object in the call and it's also going to be called with the data that is being sent from the event. So what this does is call a function on NewDoc, which takes a socket and some data. This is just going to be the string that we send the value of string from the client. Let's just put that right into the database. It takes a collection once again and it takes a document. So let's construct that document name as you recall. That should be the data and done should default to false. And it takes a callback, which is just going to be to call send docs on the socket, which basically performs a save operation on the database and does a query on the new contents of the database and sends that back. So the client will automatically update itself once the change is done. Save that. At this point, once that decides to compile, there we go, we'll be able to start adding things to our checklist. So monads, everybody needs to learn monads, right? I actually learned that yesterday. A monad is like a strain metaphor. And co-monads, you know, like jQuery, right? Because there's a user base, jQuery. People are wondering if jQuery counts as a monad. No, it does not. It might be a co-monad. That is not an excuse to not using jQuery there. Okay, and moving on. Saigo histomorphic pre-pro-morphisms. Wow. Don't do an ad. Why not an ad? There's an error message. I wonder. Let's see why that should be a problem. Let's try again. Just going to copy that just in case. Oh, right. Okay, well, it's an interesting subject. It should be done twice, right? So that is a real thing in Haskell. That is actually Haskell comedy. Don't worry if you don't get it. You get it later when you evaluate it. That was terrible. Haskell humor. One final thing. We're going to be really ambitious here. Go beyond the final frontier. Whatever Oleg was talking about, I want to understand that. Right. So we're now populated on database. Now I want to be able to click the check box to indicate that I learned, because I do know monads at least lightly. So that's a click handler. Onspand.check. Because the check box is in a span with a class of checks, so every check box now should have this handler. Oncheck gets an event. We want to get the ID of, wait a minute. I have a function called target, which gets the target elements of an event. And I have a function called data, which takes these parentheses are not balanced, right? There we go. And yeah, the data function takes an element and a data attribute name, and returns the content of that. Remember earlier, I put a data ID attribute and check span. So I'm going to know by looking at this what kind of elements, what kind of list item I clicked. Right. And that should be fairly simple. Since we already know the ID, we just emit an event on the second check and the ID back to the server. We need to listen for that event. Check. I need the timer on blank. So on check. Let's just call that on check. And that will take the socket and the ID that we sent over. So let's call Mongo again. Once again with a collection and this just takes an ID as a second argument to indicate we want to update a single document. And it takes a function, a kind of mutator function, which gets the doc in question and should just return the modified document. So a source, doc, done and the inverse of doc's DOM property. So that should just flip it between true and false. And it also takes a callback, which is once again send a socket. That should, in fact, be it. Wow! Unbalanced parentheses. That's quite great. This one's right and now we should begin parentheses. It would be so much readable, wouldn't it, if there were kind of braces. So Java people keep telling me at least. Right, let's see if that works. Let's try and check out the moment. Oh, wow! That was a spinless. Cool. And it toggles. Excellent. Let's just leave them unchecked. I also know jQuery, so good. One more thing. I mean, for one thing, I want to remove the duplicate here. And let's create a delete function in our last couple of minutes. That should be fairly straightforward. I'm just going to copy this ban. Give it a class delete. Keep the data ID for clicking and this doesn't need a stateful... There we go. I'm just going to make that an x. And like that. So let's ban delete on delete. And this is a very straightforward one. I just copy this on delete. Sends a delete and does exactly the same thing. It fetches the data ID and emits an event with the ID. So, let's have a site. Listen. We need a function that goes to the database. Soccer's ID. And this one is called delete ID, would you imagine? It takes the same parameters and this doesn't need a mutated function, obviously. So just a callback. That should, in fact, be it. Huh? Of course I knew. Thank you. Still compiles, though. Ambrose, are you going to fix this? Reload. Now let's try and get rid of one of the pre-promorphisms. And it's, by the way, quite simply if you're wondering what that means. It's just a pre-promorphism. That's both psychomorphic at the same time. I don't see the problem there. Right. So that deletes. And I mean, there's ambition and then there's just hubris. So let's admit we're never going to understand Oleg. So that was, in fact, that's a pure closures group application. Backstory, by the way. Catnip, the editor that I've been using, is a kind of closure editor with a server-side closure component and a client-side that runs in the browser. And, strangely, David Nolan asked me, that's written in closure script, right? And I had to, I mean, it felt like kicking a puppy. I had to admit it's actually written in coffee script. So. To atone. I decided that, I mean, this talk has been done before as a server-side closure thing. So I decided to atone by doing everything in closure script for a change. So. That's closure script running on nodes on the server. Communication using socket.io. I want to use create, except it's not imported to closure script yet. And let's see. Yeah, the MongoDB driver and that's also node. So there's a lot of interrupt as you've noticed. And if I have two wishes, I'm going to play the Chris Cranger game here. And I have two wishes for closure script. For one thing, why you know in core? CLJ to JS, seriously. And even better, of course, would be if there was transparent interrupt. So that when you call out to JavaScript, maps and arrays and things like that might perhaps be just converted for you. That would have been nice. Also, can the closure script compiler please run faster? Right, that's it for me. Looks like we have plenty of time for questions if there should be any. They usually aren't. There's that one. It's manipulating the DOM. That is the problem. You notice, I'm going to show you my DOM library. Every closure script project usually has something like this. It might just include JQ or Dominar or one of the many. What's the online version for closure script code? I'm focused, right? So people have been writing DOM libraries for closure script, but they all are, of course, calling out to the DOM or calling the DOM through Google closure, which is usually safer. And the DOM is going to be mutable anyway, but the guy who's sitting in front of you has done a nice job working around that for making this nicer for closure script with Web4E. Now, I just assumed that you only implemented the features you needed for your talk. So I'm assuming it's going to come. And I was too stressed out the day before yesterday to implement it myself. Sorry. Right, and I'm on.