Software Architecture

Summarized using AI

ClojureScript + React.js

Norbert Wójtowicz • March 11, 2015 • Wrocław, Poland

Summary of ClojureScript + React.js by Norbert Wójtowicz

In this presentation at wroc_love.rb 2015, speaker Norbert Wójtowicz covers the integration of ClojureScript with React.js, presenting it as a superior solution for front-end development compared to traditional frameworks like Angular and Backbone. He emphasizes the need for better tools and methodologies in programming, particularly in handling the complexity of modern web applications.

Key Points Discussed:

  • Background and Approach:

    • Wójtowicz, who has a diverse background in technology stacks, discusses the challenges in both front-end and back-end development. He focuses on solving issues through software, utilizing appropriate tools.
  • ClojureScript and React.js:

    • He argues that ClojureScript combined with React.js significantly enhances front-end development capabilities. Wójtowicz describes React.js as a rendering engine, suggesting it be treated as a black box for simplicity.
  • Data and Complexity in Applications:

    • Wójtowicz highlights the growing complexity of web applications, comparing it to the evolution from jQuery to a more organized architecture with MV* frameworks, which ultimately led to messy solutions.
    • The key insight is that focusing on data rather than just code structure helps manage this complexity.
  • Core Features of Clojure:

    • The talk explores the fundamentals of Clojure, emphasizing simplicity and efficiency, with a focus on its powerful core features like immutability, persistent data structures, and concurrency through core.async.
  • Rendering and Events:

    • Wójtowicz breaks down the application structure into rendering and event handling. He introduces core.async as a method for managing asynchronous events, simplifying the complexity often encountered in traditional JavaScript programming.
  • Real-world Applications:

    • He discusses examples of how a ClojureScript application can manage state and events efficiently, using concepts like atoms for shared state and reactive programming.
  • Closing Remarks:

    • The speaker encourages programmers to embrace ClojureScript and its tools, proposing that its architecture allows for the creation of scalable and manageable applications. An invitation to explore further learning resources is also extended.

Main Takeaways:

  • ClojureScript, in conjunction with React.js, presents a compelling alternative for building complex web applications.
  • Emphasizing data over traditional object-oriented principles can lead to simpler, more maintainable code.
  • Core.async enables effective management of asynchronous processes, allowing a clean and efficient workflow.
  • Developers are encouraged to experiment with ClojureScript and its functionalities to improve their front-end development processes.

ClojureScript + React.js
Norbert Wójtowicz • March 11, 2015 • Wrocław, Poland

wroclove.rb 2015

00:00:12.920 Okay, now we have Norbert. Does this thing work? Is this thing on? Yeah? Okay, sweet. Actually, we do have a policy only for PHP, you know. You need to have... If you’ve seen any of my talks, I’m against anything you say, basically. Yes, I don’t... Yes, I didn’t. So, this talk is about Erlang because I hate PHP.
00:00:22.360 Okay, so shall we allow him to talk about Erlang, or would you prefer to hear about ClojureScript? Okay, we can do him harm afterwards if we don’t like it, so just go ahead. Give a big hand for Norbert.
00:00:57.920 Okay, uh, I’m just going to tell you straight that this talk is about 45 minutes, maybe a little more, because I do not expect you to concentrate for a longer period of time. I’m going to try to cover a lot of stuff, and I’ll be going fast and jumping around. So, I hope everyone has had their coffee.
00:01:11.320 First, I need to know about you. How many people here have worked with a big JavaScript framework, like Angular or Backbone? Okay, sweet, so you’ve all felt the pain. How many people have done some functional programming? Anything? Sweet, Clojure, per chance? Clojure? Okay, cool.
00:01:33.399 So, who am I? I’ve worked in a variety of industries and with a range of technology stacks, from low-level to high-level, functional to object-oriented. I have some perspective on certain things, and one of these realizations is that, first of all, I’m not a front-end developer. Photoshop scares me, and even though I generate a lot of JavaScript and CSS, I don’t consider myself a front-end developer. I would never hire myself as one.
00:01:52.000 On top of that, it turns out that I’m not a very strong back-end developer either. I’ve seen a lot of poor quality code, so I know I have a developed sense of code smell. I certainly have a high self-filter, but I’ve also met many people who are much smarter than me. So, what do I do? Well, it turns out that my forte is solving problems for people and figuring out what issues they’re having, then solving it via software.
00:02:21.480 I have a real knack for drilling down to the root cause of someone’s real issue and then proposing some kind of software solution. The way I keep a step ahead of these people who are much smarter than me is that I cheat. I cheat by using better tools. And this is what this talk is about; it’s about using better tools.
00:02:50.879 We can all argue, and I’m sure we will over beers later, about whether Clojure, Ruby, Erlang, or PHP is the best solution for writing back-end software. However, the story with ClojureScript is different because ClojureScript and React.js is basically about ten times better than any other solution for front-end development right now. I mean, it just blows the competition out of the water.
00:03:03.760 So, let’s start with some armchair philosophy. A Chinese philosopher once said that not having heard something is not as good as having heard it. Having heard it is not as good as having seen it. Having seen it is not as good as knowing it, but knowing it is not as good as putting it into practice.
00:03:35.519 I don’t want you to just understand the things I’m talking about; I want you to start putting them into practice because these ideas are permanent and not specific to a technology stack. There’s this really old quote about Lisp programmers knowing the cost of everything and the value of nothing. This was from a time when Lisp was still spelled as an acronym and hardware was so slow it couldn’t deal with garbage collection. So, not very relevant these days.
00:04:08.480 However, it turns out that most, actually all, programmers know the cost of everything and the value of nothing. We basically do not question the systems we use enough. All we do is hack around them, prop them up, and make them work a little better. Sometimes, we just need to question the system, take a step back, and look for another way.
00:04:30.000 How many people have read Steve Vess's blog post about the Kingdom of Nouns? Kingdom of Nouns? No? Okay. Steve Vess is really good. You guys need to read this. You’re Ruby programmers, it’s a Java blog post, but it’s important. So, he talks about Kingdom of Nouns being the wrong thing. You need to move to the Kingdom of Verbs. Go read it. I’m not going to talk about it.
00:05:05.759 The thing is, he was sort of right, but he was wrong because the actual solution is not verbs; it’s data. When I say data, I mean things like numbers, strings, keywords, maps, vectors, and sets. These three scalars and three collections are all you ever need to model the data in your application.
00:05:22.919 This is basically what Clojure is. It’s not just a functional programming language; it’s a way of talking about your program just using these basic data structures and composing them in very interesting ways. Now let’s talk about web applications. Web applications are scaling in complexity at an exponential rate.
00:05:50.360 In the beginning, there was jQuery, and it was beautiful because we finally could build software without worrying about what browser we were in. With this new technology, we were able to build bigger and better and faster things. Because of that, we ended up with jQuery soup. At that point, we had to take a step back because the solution was not scaling.
00:06:23.760 So, we took a step back and invented MV* (Model-View-Star). MV* let us build bigger and bigger systems, and now we have MV* soup, and nothing has changed. I have seen some of these projects, and they are monstrosities. I have this feeling we’re sort of suffering from Stockholm syndrome, like the frog in the pot that's boiling to death, but the water temperature is rising so slowly we don’t notice until it's way too late.
00:06:41.680 I’m here to propose to you a different architecture for front-end development, which is basically all about rendering and events. When you start thinking about it, you need a way to render; otherwise, the application doesn’t make sense, and you need a way to handle events because everything in the browser is about events.
00:07:07.680 Our first superhero today is React.js. Who here has heard of React.js? Okay, sweet. Now I need you to forget everything you know about React.js. For those who don’t know, it’s Facebook’s library for building web skill software. The thing is, I want you to forget everything you know about Relay, Flux, GraphQL, React.js, and so forth. We’re going to do none of that.
00:07:32.039 What we’re going to do is treat React.js as a black box. Just like V8 is an optimized JavaScript engine for your browser, React.js is your new optimized rendering engine for your browser. That’s all you need to have a conversation with.
00:08:10.360 We need to set up some mental models so we’re all on the same page. The first mental model I want you to understand is that video game developers inspired us. The way video games work is that there’s a critical loop that renders the game at 60 frames per second. This is the most important loop in the game because if it doesn’t render 60 frames per second, you’ll notice it.
00:08:40.320 During the time when the computer is not rendering 60 frames per second and has a couple spare cycles, it gives it away to the application and says, 'Here, go do some work.' This is how you're going to start building web applications. The browser renders at 60 FPS. Your application does some work whenever it's allowed.
00:09:00.360 You never talk to the browser, and the browser never talks to you. That’s what React.js is for. The second mental model I need you to picture is that everything flows downstream. There’s a data model, and then you have some code that transforms that data model into a different data model, which is the Virtual DOM.
00:09:43.240 Then the Virtual DOM takes over, and React.js renders that Virtual DOM to your browser. A more concrete example would be this: you have a tweet, so you have a vector with a hash, and then you have some code that transforms it into a different kind of data structure that looks like HTML. Then React.js takes over and figures out what that HTML should look like in your browser.
00:10:02.240 When you get new data, for example, adding a second tweet to your vector, the code generates a brand new HTML page. Remember, you know nothing about the browser. So, as far as you’re concerned, you’re generating a brand new page. This is where the power of React.js comes in. React.js knows what the browser thinks it has, and it knows what you want.
00:10:41.320 It has a very optimized diffing algorithm to figure out the best way to batch update the browser. This is the same concept in pictorial form. We’re going to come back to it later. So you have code, and then you have a virtual browser, then you have HTML and the actual browser.
00:11:19.760 Let me check how I’m doing with time. Oh, I’m doing pretty good! Our next superhero is ClojureScript. When people talk about programming languages, I get this picture of Star Wars, in a sense that it’s the Rebel Alliance versus the evil Empire—Ruby versus Java, or more likely right now, Elixir versus Ruby. This is why I know that Clojure is on to something because when I think of Clojure, I never get this picture in my head.
00:12:02.040 When I think of Clojure, I think of the Borg because Clojure, as a community and as a programming language, is very unique. Their approach is basically to go into every community, every language, every library, and steal all the good ideas. They literally assimilate them. Clojure, yes, it’s a Lisp, but it’s all these other things as well. It has STM (software transactional memory), polymorphism, REPL (Read-Eval-Print Loop), and functional programming.
00:12:45.560 But notice all the way at the bottom it has things like transducers, core.async, core.logic which is Prolog, and core.typed which is Hull. These guys basically assimilate any good ideas. But what does this have to do with ClojureScript? Well, it turns out ClojureScript is exactly all those same things; it just happens that it runs on JavaScript instead of on the JVM.
00:13:22.880 I want that to sink in for a minute. Think about your JavaScript, CoffeeScript, or TypeScript. Do they give you these kinds of semantics? Because these are not just syntax changes; it’s not a transpiler—it gives you completely new superpowers. If people ask me why Clojure, I only have one sentence: if nothing else, Clojure is optimized to manage risk in the complexity of your projects.
00:14:03.680 So if this is something that you feel strongly about, then you should definitely consider Clojure. There must be a catch; it’s just too good to be true. I’m sure it’s ridiculously difficult to actually do things in a language like high school, right? Let’s talk about a Hello World application, but first I need to talk about the elephant in the room.
00:14:40.480 Yes, Clojure is a Lisp, which means it has parentheses. We’re not going to get around that. But if the one thing stopping you from actually trying Clojure is the parentheses, then I’m not even going to try to persuade you. Remember, smarter people have smarter solutions. In practice, every Lisp will tell you that it doesn’t matter what editor you have—there’s a plugin for handling the parentheses for you, so it’s a complete non-issue. Let’s say you’re more courageous; this is WrocLove after all, so you actually want to learn Clojure.
00:15:24.320 But to understand the slides, you actually need to learn Clojure, so I’m now going to teach you Clojure in 30 seconds. This is a comment. This is a function call, a function with arguments. This is how you define a function with an argument. Here I’m defining a VAR— that’s a hash with keys as keywords—and a string, a vector, and a set.
00:15:59.720 This is some syntax just for fetching things out of a hash or setting new values inside of a hash. This is an example of some syntactic sugar within a function. Now you know Clojure. Seriously, that’s all there is to it! But except for understanding the syntax, you need an environment, and this is where Boot comes in.
00:16:54.800 So for all of you Ruby developers, Boot is Rake plus Gemfile plus Bundler all wrapped into one, with a little hint of Capistrano. Basically, it sets up your Clojure environment, determines what version of Clojure and ClojureScript you should be running, figures out all the dependencies, loads everything, and gives you access to the REPL. Through normal function composition, it gives you a way to define custom tasks like you would in Rake.
00:17:32.720 Now, there’s a huge asterisk next to that because there’s also Leiningen, which happens to be the de facto standard in the Clojure community. It does all those exact same things but has a very different internal implementation. The reason I’m talking about Boot in this presentation is that, first, the configuration will fit on a slide.
00:18:18.319 Secondly, if you have not already drunk the Clojure Kool-Aid, then I feel that Boot right now has a better story for someone who wants to get started with ClojureScript and doesn’t really want to deal with the JVM that much. Similarly, we need a wrapper for React.js because we don’t want to deal with React.js specifically. It has way too much of an object-oriented API.
00:19:04.560 So, Rum is what I’m going to be talking about in this presentation. But again, huge asterisk: Reagent and M are much more mature libraries as wrappers for React.js. If you’re just going to get started, I’d really suggest going with Reagent; just go with Reagent.
00:19:50.080 The reason why I’m talking about Rum is that, first of all, I really like the semantics it offers, and the second reason is that, because of the semantics, it can emulate all those other libraries. So, in order to get a production-ready ClojureScript application into production, you need four files: an HTML file, a CSS file (which is totally optional), a ClojureScript file, and a build.boot file.
00:20:32.080 So, for anyone who’s built a Rails application or Backbone or Angular or whatever, seriously, this is all you need! Simplicity! Remember my lighting talk? This is the HTML file. Notice that it has no mentions of ClojureScript or even development and production environments.
00:21:09.720 This is the HTML that will work in production, and Boot injects things that you need for development—for your convenience. This is your Hello World ClojureScript application. Notice there’s a namespace. If nothing else, just having namespaces in JavaScript is already enough for me to switch over.
00:21:48.600 This has a namespace hello.core. It loads a library called Rum and then defines a post, which is just some data, and defines a component. Then all the way at the bottom, it mounts that component onto a specific part of the HTML we decided we were going to do.
00:22:29.480 Rum defc is a macro, and basically what it does is it takes the body and creates a React.js component class, like a proper one. You can use it outside of ClojureScript, and nobody will be the wiser. So, basically, we create this component called render-post that just returns a block quote text with an author.
00:23:03.120 This is build.boot. All it really is is a couple of libraries that I’m interested in, a bunch of development helper tools, and then the tools provide these tasks. Those tasks can either be run from the command line, or since they're functions, you can just run them in the REPL or wherever.
00:23:42.320 Since they’re functions, you can compose them. For example, you can build a Dev task that first of all watches your HTML, CSS, and JavaScript files for any changes. Serve will run an HTTP server, so it’ll actually run your server without needing to run a separate one. Remember, we are talking about building a ClojureScript application, so we don’t want anything to do with Java.
00:24:23.440 Then it’ll set up a REPL for you. This REPL will connect to your browser. I know—science fiction! From cljs.js is the de facto way of loading third-party JavaScript libraries in a very sensible way. cljs.js is the actual compiler of your ClojureScript file, and then reload will do that thing I mentioned; it will upload the changes via websockets, and only change those things that have changed.
00:25:05.680 This is a more refined version of that hot reload swapping we saw in yesterday’s lightning talk for React.js. So if you’ve stayed with me here, in five minutes we generated a development environment that’s already better than anything you have.
00:25:46.760 On top of everything else that it does, it includes auto-reloading via websockets that doesn’t break your application's state, and it includes a real REPL plugged into your running browser session.
00:26:11.200 Notice that when I tell ClojureScript to add a number to a string, it tells me it’s a bad idea, but it’ll do it anyway because it’s JavaScript. If I run alert hello, it will actually pop up in my browser! This is not even a REPL into like Node.js or something; this is a REPL plugged into the running browser session in my tab.
00:26:57.760 Okay, so we built a Hello World app, but I’m here selling you that this is a way to build huge, complex applications. Remember, applications consist of rendering and events. So let’s talk about rendering. This is that thing we did where we saw that this is our entire application. Basically, this is all we have to do: we have to figure out how to get data that makes sense to us and a way to transform it into the DOM that our designers will be happy with.
00:27:54.440 Rendering is a data model, a data transformation, and no step three, because as soon as you transform the data into a virtual DOM, React just takes over and only in a very optimal way batches updates of things that need to change in the browser. But we can do better than this because a lot of the time if nothing has changed in our data model, we know nothing will change in the browser, so we can actually tell React to do no work.
00:28:44.360 So React won’t even have to run a diffing algorithm because it won’t even execute. This part of the talk is to consider different semantics we can provide in an application for talking about what it means for things to render on change. Rum has this concept of Mixins, and Mixins are ways to define custom semantics for what it means for your components to change. You, as the application developer, know best because it’s specific to your application.
00:29:36.960 A really simple example is Rum static. ClojureScript gives you immutable data structures that are persistent. If you’re from the Haskell community, this means that every time you change a hash or update a vector or whatever, it doesn’t update it; it returns a new value, and the old version stays the same. If this sounds like crazy talk, it’s actually very performant because once a programming language can guarantee that you can’t change things, it can actually share a lot of the state.
00:30:48.960 So this is actually much better than copying a lot of times. Here’s a component label that we define as static with a static mixin. The way it works is that static means you’re telling me the only way something in this thing might change is if the arguments are different. So if I run 'label hello' twice with the same argument, it won’t even run the rendering function the second time because it knows that there is no reason for it.
00:31:28.800 Because we’re talking about mutable data structures, this is even faster than if we did it in React.js native because in JavaScript, you have to do a string comparison, and here, you do a pointer comparison. Rum reactive is a different kind of mixin and models Reagent. If you’re using Reagent, this is the approach you’re using basically everywhere.
00:32:12.000 To understand that we need to talk about atoms. So Clojure provides different concurrency models about how to think about concurrency in your application, and atoms just happen to be one of them. They’re a way to share independent mutable, synchronized, basically immutable shared data. They give you the ability to do atomic updates, which means things can read and write at the same time, but you’re guaranteed that the update is atomic.
00:32:55.760 It gives you other features like the ability to set up watchers. You basically say anytime someone changes this atom, I want to be informed about it. So you can use this knowledge while building your components. You can, for instance, define a to-do atom, which is some data, and you define a component item that says it’s reactive and it will react to some state.
00:33:42.040 So this state is the span title. Rum react state, and then you mount it at some point in your application. Anytime someone changes this data, this component automatically knows that it needs to update itself. There's no orchestration involved; it's automatic. The effect is similar to two-way binding in Angular or KnockoutJS, but without the performance issues. It's always a one-way flow.
00:34:24.520 Another common trick for atoms is that sometimes you’re talking about different components in React that need to share some state. But it’s not a real global state; it’s something that a couple of components need. This is an ideal example where usually we’d just use an atom to say that these couple of components need to share some data, and then we let them.
00:35:00.760 It’s sort of like local global state. Anytime the input changes, it’s going to reset the value of the atom, and then when the search button wants to use it, it always knows it’s up to date. So Rum cursor is... if you want to use Om, you basically use cursors.
00:35:39.360 I don’t think I’m going to go into the code for cursors because I don’t think it’s the best approach a lot of the time. You can read more about it, but one important thing we need to talk about in Om is that, first of all, Om was the first real React.js wrapper for ClojureScript that made it into the big scene.
00:36:19.160 The reason why it made a huge wave was because some of the consequences of having a one-way flow and having all of your state in the beginning influencing your actual page had very interesting side effects. For example, it’s very easy to do infinite undo in an application. Because when something changes, all you do is save that old version of the atom somewhere, like on a stack, and when someone wants to do an undo, you just pop it off the stack, and essentially you get infinite undo for free.
00:37:03.440 Because of immutable data structures, it doesn’t take up a lot of memory. Another thing you can do is we're talking about event streaming. People who use Om and cursors can model their application as a set of events, which has very nice consequences. Besides working well inside the browser, you can send those events to the backend, so you essentially get event streaming for free.
00:37:43.360 Another really cool trick for development and other issues is since your entire application is a consequence of this data structure in the beginning, you can serialize this data and do interesting things with it. For example, you can save it to local storage and the next time someone opens the page, it would get that same view. Or you can send it over the wire, and in a different browser, you can open it up and see the application exactly as that user saw it, with all of the state.
00:38:30.880 This is cursors. Cursors solve this problem of if you have this entire application in one place, sometimes components have to fetch various parts of data, and cursors try to solve this problem by being these pointers of indirection. I don't think this is a super great idea on a large scale, but that's the thing about Rum, because Rum is this concept of 'we don’t know what you want, so we’re just going to let you do whatever you want.' You can mix and match these in an application.
00:39:08.280 Some parts will use reactive; some parts will use cursors, and a lot of them will use static because they’re just dependent on the arguments. You can mix and match this stuff as you wish, which is nice because in Om and Reagent, it sort of pushes on you what kind of storage mechanism and syncing mechanism you need to use.
00:39:45.520 The other nice attribute of this is that you can actually build your own Rum datascript. Rum datascript is a great example of this. Who here knows about Datomic? We’ve got some people up there! Datomic uses a query language that is similar to SQL, but different. I'm not going to delve too much into the details.
00:40:27.040 One of the reasons it's really popular now is because Datomic uses it as its query language. DataScript is a port of this data log concept to the front end. The guy actually ported the entire query language, and you can have an in-memory database in your browser and build your views as queries on this data.
00:41:11.880 So, I often get this question about how GitHub used to do things, where they had one data storage, then split into a bunch of stores and did stuff, but then they realized that this created complexity, and they didn’t know what to do with it. Now they are moving away from it, and it's similar but different: you have a data store, and then you can build queries, and it is always up to date.
00:41:54.240 You can model very complex situations easily. Imagine if you didn’t have SQL and had to do it in imperative code. So why, in the world, would you want to do that? Well, here’s an ideal CRUD application: this is GitHub Issues. This is not specific to GitHub; this is how a lot of our applications look. You have a list of things, and then you have a million things you can sort and group by.
00:42:36.440 Oftentimes, the way this works is that if you set a specific milestone, it has a side effect that the author dropdown should change because it should only show the authors that are relevant to that milestone. The way GitHub does that is that every time you click something, it sends an AJAX query to the server with a huge list of params, and there’s a Rails endpoint on the other side that understands these front-end things and figures out how to do things.
00:43:21.080 It returns a list of stuff, which is silly because even something like GitHub, which is huge, definitely has under 10K issues. If you just took all of this text and all of the elements you can sort or group by and sent it over as one payload, we’re talking about kilobytes of code—100K even if it was half a megabyte. That’s like sending a single JPEG!
00:44:03.440 You could send it over once, and then you have real-time UI. If you think this is science fiction, here’s an actual implementation of it. There’s something called Achacha, and what the guy does is the idea is sort of like a GitHub achievements kind of thing.
00:44:45.960 The way it works is the first time you load this page, it loads; if you open the console, it will change over time, but right now, I think it was around one megabyte of data. It literally loads up one megabyte of data into your browser, and then all of the pages, whether it’s a profile page, a repo page, or the dashboard, are all done at runtime via queries.
00:45:34.400 The wonderful thing about DataScript and Datalog is that it has this beautiful property that it’s reversible. When you have a component and you said that this component renders using this query, the way Achacha works is that it fetches all the data first—but then it opens a websocket to the server.
00:46:13.200 When new data comes into the server, it will inform your browser via websockets that it has some new data. When your browser gets this data, it needs to re-render the page because it gained new data. So, what do you re-render? The naive approach would be to re-run all the queries.
00:46:56.520 However, because Datalog is reversible, you can ask each component if, given this new data, it would change. If it says yes, then you say, 'Okay, here’s the new data. Rerun your stuff.' So, this is yet another crazy idea about rendering on change.
00:47:37.560 Once your abstraction is that we don’t know, because it’s application-specific, you can provide your own semantics. Now let’s talk about events because that’s the other half of this coin. We’ve built this application, and if you haven’t figured it out yet, I want to point this out. Once I’ve built this thing that basically says given some data, this is what I want the web page to look like, you’re done.
00:48:13.840 No matter what I—I never have to touch this code again. I can unit test all this stuff, and I know that whatever new techniques I come up with for fetching and changing data, I never have to change this code.
00:48:55.920 So let’s talk about what happens before data. Before data, you have a bunch of events, so you have AJAX and websockets, user input, browser events, and JS events. You probably have a million other things happening. But you want simplicity; you want a simple way to say there’s only one way to generate my data.
00:49:52.560 This is where our next superhero comes in called core.async. What is core.async? Core.async is basically stealing Go’s CSP (Communicating Sequential Processes) and putting it into Clojure.
00:50:29.520 The beautiful thing about this, as a side comment, is that they did this without changing a single line in the compiler. They wrote this as a library that completely changes the semantics of your language. This is the power of Lisp that people keep talking about, but Clojure was really the first to deliver on this promise.
00:51:09.320 What’s this core.async stuff? How many people have done something with it? Alright, let’s say we want to start up an infinite loop. The first line starts up an infinite loop that yields every 250 milliseconds. After I start that infinite loop, I want to start a second infinite loop, a third infinite loop, and a fourth that fetches the data I’ve been generating and does something pretty with the HTML.
00:51:47.720 Can someone tell me how this works in JavaScript, where there are no threads? The way this works is 'go' is a macro. It’s a very ugly macro—there be dragons inside—but as a user, you don’t need to know that. The way 'go' works is it takes this code and rewrites it; it figures out what stuff is asynchronous and rewrites it into callbacks.
00:52:32.440 If you’re on the JVM, you can choose whether you want to use threads or green threads. In JavaScript, you need to use callbacks because there are no threads. So, basically, you can write code that looks synchronous but actually behaves asynchronously. You’ve completely hidden all that callback hell complexity, flushing out the real logic of your application.
00:53:07.520 So why would you want to do this? Well, once you start using core.async, everything in your application is a queue. You use a queue everywhere. If you’re not sure what to do, just add another queue. This is an example: imagine you have a requirement that a user gives you input and you need to query a web image API and video API, but you’re only interested in the first result.
00:53:44.880 So you make three AJAX calls, and all of them are going to return at some point. But you’re only interested in which one is fastest. On top of that, because you really care about speed, you’re going to store that information in local storage, and next time somebody does that same query, you want to fetch the local storage version. Because we care about speed so much, if you can’t deal with it in one second, I just want you to give up.
00:54:21.760 Imagine those requirements in JavaScript? All of those callbacks depend on each other because it’s not enough to have an on-success; you need to have an on-success before someone else has an on-success. This is not pseudo code—this is exactly how you do this in Clojure. You basically create a channel.
00:54:57.720 Then you create a second channel. T.timeout is a function that returns a channel that says after one second, I’m going to put a value onto this channel for you. Then you create these go loops and say each of these things will run a query and whenever it returns, it will put it onto this channel.
00:55:53.920 What’s more, the alt function basically says, 'Given these lists of channels, I’m only interested in the first value that comes from any of them,' and I want to block until that returns. Notice that all these 'go' functions work because they don’t block; they start running code in the background, allowing you to write non-blocking code.
00:56:45.200 This is amazing! You guys are not as happy about this as you should be! So, if we move up in architecture, right? We haven't changed what core.async does, but in truth, it also creates a new way of architecting because I basically have a channel everywhere. Every time we talk, we have to basically communicate with components.
00:57:34.760 For example, how you do it in Scala with actor models is there’s always something needs to send a message to something else. This is a problem. This is better than other systems, but the problem is that this thing needs to know who to send the message to. It needs to know what mailbox it corresponds to.
00:58:16.840 What we do is introduce a third concept. Between this component and this one, we introduce a channel. The channel can have its own semantics: it can be blocking or non-blocking, buffered or non-buffered, broadcasting or multiplexing. It doesn’t matter from the component’s perspective. My input and my output is a channel; I process my data and, when I’m done, I put it on a channel.
00:59:07.520 I have no idea where this information came from, and I don’t know where this information is going. That’s for someone orchestrating to figure out. This is what I meant by de-complexifying components. These components no longer know anything about each other and can be reused for lots of different things.
00:59:35.520 Last time I talked about this, someone asked, 'What about promises?' Well, I hope I’ve somewhat indicated that core.async is more than just a nicer way to avoid callback hell; it gives you a new metalanguage to talk about building systems. This concept of channels with their own semantics means this is more than just promises.
01:00:14.240 So, that’s how we solve the problem of having lots of various things talk asynchronously: core.async figures out how to make all those things work in sync.
01:00:39.280 Now, for something completely different. I wasn’t sure how long this talk was going to take. I’m actually quite satisfied with the time. Here are a bunch of things I didn’t mention, but you should definitely check out.
01:01:10.160 I can run through them quickly: Transducers are basically a sci-fi version of innumerables; I can explain more over drinks later. Core logic is an implementation of Prolog. Core.match is pattern matching, real pattern matching.
01:01:39.920 React native: in the future we’ll all be using ClojureScript for our iOS development as well. If you’re courageous enough to do Clojure on the backend and ClojureScript on the front end, definitely check out Sente because it handles all the boilerplate code for your backend talking to your front end.
01:02:21.840 It automatically figures out how to do Ajax connections and can upgrade or downgrade itself. Transit is a must if you’re doing Clojure’s code and you need to parse JSON. Do not use the default JSON parser; use Transit.
01:03:12.960 Lastly, there’s this concept of incidental versus inherent complexity, which I wish I had not forgotten to mention yesterday. These are terms that everyone needs to start using.
01:03:54.760 Incidental complexity is the kind of pain we cause ourselves due to the way we build our systems, while inherent complexity is the complexity that is concrete in our domain, given the circumstances of our specific contexts.
01:04:17.840 That’s about it. I should probably backtrack a little bit. Many people are excited about ClojureScript but don’t know how to get started, and I keep hearing about this.
01:04:58.800 So, I’m working on some course material basically to take your hand and walk you step by step, pulling you into this insane vortex that you will never leave. I have this idea for something like a ClojureScript From Zero to Hero.
01:05:26.080 In true spirit, I still don’t have a landing page up yet, but if you go to this link, there’s a MailChimp sign-up. So if you’re interested in figuring out how to start implementing this, please sign up, and I will definitely be emailing you material.
01:06:28.280 So, you mentioned that in core.async on the JVM, you can choose whether to use green threads or real threads. Does that mean that when the browser environment eventually gets thread support, you will get thread support in ClojureScript for free?
01:07:28.120 Yes, in theory, if the way web workers changes and allows application code to use them better then yes, that’s possible.
01:07:45.520 Just to highlight that, it’s not magic, and if you want to use real threads versus green threads, you need to use different methods, right? Clojure is all about simplicity.
01:08:03.200 So if you want to specify that you want to use green threads in one instance and real threads in another, you have to be explicit.
01:08:33.240 But the real problem right now is that we have these things called web workers, but you can’t really use them sensibly from application code.
01:08:52.640 One more question—yes or no questions only!
01:09:09.760 Can you share code between Clojure backend and ClojureScript front end in any way?
01:09:16.320 Yes, often you don’t share code. Instead, you share data. However, there is something called cljs, which means that 98% of the code you use is the same on both sides.
01:09:35.520 As long as you don’t call methods specific to their respective runtime, you can share resources effectively.
01:09:51.120 Thank you very much—let’s give a big applause!
Explore all talks recorded at wroclove.rb 2015
+6