 Wow, okay. Where did it make me feel welcome? That's awesome. Okay, so I'm Edmund Jackson. I'm an independent consultant. I do statistical stuff and data analysis. I'm here to talk about doing that in closure, so data science enclosure. So let's start by defining our terms. What is data science? Or what is a data scientist? And a wag on Twitter once said that data scientists are an analyst who happens to live in California. I think there's a lot of mileage to be had in that one, actually. But there is something new, and there's something very old as well. Let's start with the old, because this is closure, like our roots. When you think back to what data science be, in fact, it's that realm of endeavor that requires simultaneously advanced computational and statistical methods. So you have to do both of these things together at the same time. And I think that's sort of the defining thing. And if you define it that way, you think that Alan Turing, he started the whole of computer science for this very purpose. I mean, he was trying to decode German codes in real time under conditions of bomb-falling duress. And that was what inspired him. It wasn't lolcats. It might be hard to believe when you see the internet these days. But that was the start of it. And this tradition has continued. Things like atom smashing at the LHC, or radio astronomy, protein crystallography. These are scientific endeavors that require both very advanced maths and very advanced computers working together to solve a problem. So it's been around since the beginning. So what's the excitement? What's the new thing? All those examples I've given you have required billion-dollar government research budgets and armies of PhD zealots to solve them. And what's new now is that that's no longer the case. The advent of things like computing platforms, which give people very powerful computers to do large-scale calculations, open-source platforms like R, which give you the tools to actually perform statistics in the hands of every man, and large amounts of data accruing in private companies as well as being opened up from the public sector to do statistics over. All of this is democratizing this activity. And that's the excitement. It's kind of like the early days of Linux where, you know, the stuff came out and people could just hack on it. And, you know, the original UNIX high priests had a bit of a giggle up their sleeves, but they were wrong, right? And it's the same here. Like, the stuff is coming out. It's in its early days, and I think it's going to revolutionize stuff. So let's talk about analysis here, right? So what are the... How do you summon forth knowledge or understanding from within data? What are the tools, the sigils of doing that? And, like, the first spell would be Excel. And I'm not really joking about that. It's like an interactive, functional, reactive programming language with an embedded maths, DSL, and graphical capabilities, you know, what's not to like, you know? It's the most widely deployed analytical platform on the planet. The trouble is it's sort of possessed by this demon paperclip who will, like, turn your floating point numbers into dates and, like, not tell you, right? And if you've got a completely opaque computational model, I mean, you know, the spreadsheet now takes 10 minutes to run. Why? There's no way to find that out, right? So more serious tools are things like MATLAB, which grew out of linear algebra. It's very good at that, doing that sort of thing fast. It's very big in the engineering community. Mathematica, which is symbolic manipulation, so symbolic integration, differentiation, that kind of thing. R, which grew out of stats. And then, sort of, more specialist tools like SAS and Stata, but they're not really programmatic. So that's, like, sort of one group of tools that are used to do analysis. And then the other stuff is, like, C++. All of the heavy lifting is often done in C++. And the, you know, the heart of darkness itself, Fortran, which underlies most of the rest of them, in fact. Like, very frequently, if you go deep enough down the stack, you find Fortran there. Okay, so these things solve one part of our problem. So they have, like, mathematical abstraction. So they give you an interactive environment. And, you know, like that talk we saw yesterday about the neural networks, that's absolutely critical for doing any sort of mathematical exploration. You have to be able to poke your data, or product it, throw some curves up, change it, you know, play with it and see what's going on. You can't do it without it. It's a sine qua non. And you need visualization as part of that. Then the actual tools that they provide you, things like linear algebra, statistics, machine learning, and optimization, all of those are available as libraries in these sorts of platforms. But that's not enough for data science. Data science is more than math. It's data, right? So you have to be part of an environment or a context. Like, either you have a large amount of data and you have to get at it, or you're within a business process. So you need more than mathematics. You need to be part of reality plugged in. And that's the second requirement, which is platform. Okay, firstly, you need speed. I mean, that goes without saying. But then, it's data, data, data, right? The analysis, like, previous things, like your mathematical platforms, it tends to be a case of someone will give you the spots of data, and you go in the corner and you analyze it, and then you come back and you tell them the answer. And that's not really applicable for data science, because the data's coming in and you've got to embed yourself in a business process or keep up with it. And so you have to deal with things like XML. And what are you going to do with XML in MATLAB, you know? Put it into a matrix. It's just that sort of, you need zippers, right? You need proper data structures to perform the analysis in its native way. Speak on its proper level. You need the ability to get at the data. So proper access to things like databases, queues, REST APIs, all that kind of stuff. This is not really available to me like Mathematica. You just, it's not there. In addition, because you're part of a larger context, you're not hiding in the corner with your box of data, you need to have end-to-end quality control. So things like tests, integration, deployment, monitoring. So data science requires both, right? You need the mathematical stuff and you need the platform stuff. That's sort of not really available in your traditional things. I mean, they kind of segregate out like that. And the reason is our demon friend complexity, right? So if you're working in C++ and you're having to worry about buffer overruns, you can't be worrying about the condition number of your matrix at the same time. Very few people have a mental capacity to do that. They exist, but they're kind of rare. So on the other hand, if you're working in Mathematica, and it's giving you the tools to perform a numeric integral, I mean a symbolic integration, it probably doesn't give you handles to make that thing fast or to get to the nitty-gritty, because it's hidden away from you. So compromises have been made, and it separates out according to that curve. The more platform power you get, the less analytical power you get, and vice versa. So you have this situation. You have two layers. You want to get to the top. Analytical stuff gets you halfway up, so you can analyze your data and get an answer, but now you can't scale it out. Plus, if you have your answer, you can scale it up and do useful things, but how do you get it, right? And, you know, this was me for a long time trying to solve that problem. Now, this is a crying need, so obviously there is an existing answer, and that answer is Python. The Python community has really stepped into this. They've got a really nice virtual machine which gives you good access to databases, and it also has strong libraries like NumPy and SciPy that give you good analytical capabilities. So they're really, like, forging ahead and they're getting a good following in this particular domain for that reason, because they're sort of in that sweet spot. But, you know, the serpent is never to be trusted. And the reason... Yeah. I like Python, really. The reason is this. I mean, what's the difference between this and this? I mean, you know, Kevin will be into that. I mean, it's all about typography, right? Written like that, that's latex, right? I'm talking about maths. There I'm talking about programming. And this is like, it's subtle, it's a rift in the ideology. And something like an ideology? Wow. In a programming language, sorry, in mathematics, this makes sense if and only if X is an infinite value. It does make sense. If X is infinite, that's true. In an imperative language, that makes sense whenever X is a number, and in Perl, whenever X is anything, right? So the point is, like, putting aside, like, differences between equality and assignment, I know these are different things, but fundamental disagreement between the model, the domain you're trying to think about, which is mathematics, and its mode of expression in a language. And that's a difficulty. When you work in an imperative language, you're doing this, you're building the machine to solve the problem, rather than solving the problem itself on its own terms. So you're saying, you're trying to decide how is this machine going to work? You know, the lever turns three and a half degrees and it shoots the toast up to the toucan and what you program is a machine. It's a description of a process and the nitty-gritty thereof, rather than being in the domain of your problem which is the mathematics itself. So it's like logic programming. You want to be in the domain of the logic, describing the logical operators, rather than the search itself. So that's problem number one. Problem number two is that, you know, it's got mutable data. So I won't spend long where this is like old hat here, but it's like, you have this data, and you hand that around. But now people can't get the data, so you have to get them somewhere to get the data which you've just locked away. And so you build these elaborate patterns, and I use that word quite specifically, around the data. And what you end up with is like this Fabergé egg with a toffee center at the middle. And the problem is that that's like giving yourself extra trouble to deal with. You want to deal with the data as data, like in its native format. You want maps and hashes and vectors programming. I mean, we know the answer to these problems, both of them, because it gives you immutability and it changes the way you're thinking about the problem, such that it's closer to the maps. The domain that you're expressing and its mode of expression are much closer together, so it's a natural expression, and it makes you more able to deal with mathematical problems. In addition, you have the serious data abstractions. You know, I spoke earlier about zippers for XML, but in any of these sorts of things, you need to have proper ways of dealing with the data on its own terms, in some kind of a wall. That's solving the mathematical side, but you need that on the platform as well. So you need to have it in some sort of powerful thing that gives you the ability to connect to business processes, data sources, and to have quality. And there's a group of languages that satisfy all of this. You know, Clojure is one of them. Scala, F-sharp are a couple of other ones. And the simplicity dividend that they yield as a result of being functional allow you to sort of cross this event horizon of complexity. And there's a large amount of mileage to be had in learning these things and solving problems like data science with them. Okay, so that's sort of like the fundamentals why is Clojure intrinsically useful for solving this kind of problem. Down to practicums. What particular things does Clojure bring to the party? So in its native Clojure, you have a bunch of libraries like Encanta. We saw a lot of that yesterday for throwing up curves, interacting with things. Basic statistics, basically, in your algebra. This gets you a very long way. Then, for large-scale problems, storm for distributed computation, cascolog as a nice data log abstraction over Hadoop. Atomic for bringing the data into your process so that you can handle it locally. And there's a bunch of other stuff, but these are like the leaders. Now, if you're trying to solve a problem and your library doesn't exist in Clojure, you can reach out to the JVM and get it there. And what exists there for data science type problems? Well, I've mentioned Hadoop already, but it's closed over by cascolog, so you wouldn't want to use that natively. Mahout, or Mahoot, Mahout, I don't know how to pronounce, that's machine learning on Hadoop. And that's available on Java, and you can get to it easily from Clojure, and there are a couple of really fun blog posts about that. The good part about that is is you can run that from within your Clojure system, call out to Mahout, get it to do the analysis, bring back the numbers, and see them in your graphs, in your interactive environment. And natively, you can't do that. That's really powerful. Hadoop is a long-standing machine learning package from, I think, his Waikato University in New Zealand, which has got lots of very good machine learning type things. There are others like Incog and stuff out there. JBLAS is linear algebra. It's a very nice compromise between calling out to Fortran on the native platform and keeping things in Java. So, you know, Zach was speaking yesterday about the difficulties of this, and I don't really know that much about it, but it's massively difficult. JVM, in order to get native speed, you have to copy them out, and that's like an order-in operation. So, for linear-time things, you keep them on the JVM and solve them in Java. And if it doesn't exist on the JVM, what you're trying to do, everything talks to the JVM, so you can take one step further out and use a JNI. Something like RNCANCH is a good example. You can actually, like, take your Clojure data, push it across to get the analysis done in R, and then pull it back in again. I mean, there's a massive performance penalty, and not many people are using it, and I've used it successfully for real problems. Right, so Clojure solves all these problems. It gives you everything. There are a few things that are missing, like optimizers. You know, we don't have really a good answer for optimizers yet, but your tools are there, your mathematical side is there, and your platform is there. It gives you speed, good language constructs, good data abstractions, and concretions. That's a good answer, right? So, okay. That's like part one of the talk, sort of like the hand-wavy, everything is awesome. You know, let's actually, like, get down to some brass tacks and see some code. So, I'm going to start by going into one of my favorite tools from information theory, which is entropy. And it's not very widely used, as far as I can tell, and I thought it would be a fun crowd. I thought you might enjoy a bit of an adventure in that. So, let's start with English money. It's different to American money. It's worth more for a start. But more than that, because when you flick an English coin, it comes up heads 60% of the time, and the reason is because Her Majesty's profile is on there and she exerts a certain royal prerogative to be seen more frequently. Right. So, if we're playing a gambling game with English money, and I'm flicking the coin, I'm going to say, you know, what happens? You'll predict heads will come up because there's a 60% probability of that occurring. So, you predict heads. I flick it again. It's an independent event. Well, you make the same bet, right? The third time, you make the same bet, right? Ten times, right? Something's smelling, right? This is the sequence of the ten... Oh, sorry. This is the single sequence of ten flips of the coin, which is the highest probability. But intuitively, you can smell that something is wrong here, right? Because you're expecting 40% tails in there. And indeed, if I flip one of these heads to a tail, my total probability does go down. And the mistake is thinking that we live in the world where the most probable thing happens. You know, this is like common sense. This is not how the world works. What your intuition is telling you is don't match up. So, you've got your generative distribution, which is what you expected to happen. 60% heads, 40% tails. And you can express that as this histogram or this density here, which says, this event heads has a probability of occurring at 60%, and tails here are 40%. And what I saw was 100% tails. So, your intuition that something is smelly here is that these two distributions are not the same. There's some difference between them. And to get a mathematical handle on that, we have to take a step back. And the fancy-pants word is self-information. But all it is, is if you have an event which has a particular probability p, minus log 2 of p gives you the unexpectedness of that event. So, if you have an event which has got a probability of 1, it's totally not unexpected. So, it has an unexpectedness of 0. If I flip my coin and it comes up heads, that is a probability of 0.6. So, it's over here. It's not really unexpected. 40%, slightly more unexpected. If I flip the coin and it quantum tunnels through the floor in front of my feet, well, that's kind of unexpected, right? So, it's got a... It can happen, but it's got a very high unexpectedness. So, that's like the first unit of that. Now, that's a single event. But let's talk about collections of events or distributions. If I have many events, I can describe it as a distribution. And if I take the average of this unexpectedness, that log 2 of p over the whole distribution weighted by the probabilities. So, minus p log p, I get this thing called entropy. Fancy word for unpredictability. So, it says if I have a distribution, what's the unpredictability of drawing things out of that distribution? So, if I have, you know, equality-loving American money, 50-50 chance heads or tails, it's totally unpredictable when I flick it. Will it be a head or a tail? There's no edging gambling on that. Whereas, like, with my English money, it's slightly more unpredictable. So, it's entropy... Sorry, it's slightly less unpredictable. It's more predictable. Triple and double negatives always strip me upright. And therefore, a lower entropy. My coin, which came up 100% of the time, it's totally predictable. So, it's got an entropy of zero. So, in summary, minus p log p gives you the entropy of a distribution which tells you how unpredictable draws from that distribution are. And you can say, okay, I can see now these distributions have some difference between them. Can I get a closer handle on that? And indeed, you can with a thing called relative entropy. So, this is the last mathematical slide, I promise. So, instead of minus p log p, this is p log p over q, where you have two distributions, this is distribution p, that is distribution q, and you average over each element from that. So, to get the relative entropy between these two things, it's p log p over q, where, like, 0.6 times log 0.6 over 0.6, plus 0.4 times 0.4 over 0.4, right? Those come out as zero. So, the distance between these two distributions is zero, as you'd expect, because they're the same distribution. If they're different distributions, that comes out as a positive number. So, like, long story short, this is the lead. This is the important thing. Relative entropy is a simple equation, and it allows me to take the distance between any two distributions of any parametric form and doesn't have to make any clever assumptions about them, provided they're over the same space. So, it allows me to ask very general and open questions about distributions. Okay, so that's the summary. Entropy is the amount of unpredictability of a distribution, and relative entropy is a distance between two distributions on the basis of their unpredictability. Okay, so that's the mathematical abstraction. So, we've got some cool maths. That's, like, the science part of the data science. Let's now see how do we express this in closure. So, I have some closure over here. Here's some I cooked earlier. And, of course, Emax is going to fight with me now. Come on, Emax, you know you want to. Can everyone sort of see that, to some extent? Yeah. So, okay, let's go through it. I'm going to actually see this on my own screen, so I'm going to have to rearrange things at the moment, yeah. Okay, so let's sort of, by getting some, something on my screen, okay. So, let's sort of with some random numbers. I have a million random numbers, and they're in this vector called p's. And I want to say, well, how would I calculate the entropy of that collection? The most naive thing to do is to say, let's define a function entropy, which is just p times log p, direct translation from the mathematics, and now I want to go over the entire distribution. So, map entropy over p's, and then reduce with plus, so that's sigma p log p, all right. And if I take, if I benchmark that, up until yesterday I had somewhat different code, I just said time in the front there, but Zach Tellman taught me better. So, if you actually run proper benchmarks, you'll see that on average, it means to run over a collection of a million. Now you say, okay, well that's nice. Let's try to do it a little bit faster. So, the closure gives me a handle, which says, okay, I can't use native Java arrays to make it faster. So, the code changes slightly. Here it is. It's the same thing. It's expressing a reduction over the collection p's, right. Whoops. One handed emacs, nice. Okay, so you get the element from the collection, and reduce over it, adding it to the accumulator res. If I benchmark that, you can see I've halved the time to about 25 milliseconds. If that's not good enough, we can use the most awesome reducers, ooh, reductors, no, reducers. The reducers library, and that gives me access to the fork join queue, so I can take this thing, break it up, put it onto a fork join queue, and use all of the cores. I mean, this is a laptop, right, and it has like eight virtual cores on it. This is like, we've got to get real with this. Reduces gives me that power, and I can take the calculation time down to 8 milliseconds. It's a huge reduction, absolutely massive. And Closure gives me that handle, so I can write my maths cleanly, and I can make it go quickly, and that's huge. So to compare it, oh, yeah, there's another one, I could try and look at the native Java implementation. So Clatrix is a JBLAST wrapper, but because this is a linear time operation, it doesn't actually go off the JVA, it actually writes it in Java. And so this is an expression, what it does, it takes what I've done, I've taken my million numbers, turned them into a column, and then I've applied log element-wise to that collection, and then taken the dot product between p and log p. So it's just a different way of expressing the same maths. Dot is kind of like a mathematical reduce. When I benchmark that, I get 25 milliseconds, which is exactly the same number as I got when I wrote the Java area reducer, which is what you'd expect. If I compare it with stuff that's available out there, MATLAB, you know, 12 milliseconds. So on a single core, it absolutely smokes us, and that we should expect because it's able to call out to Fortran natively. But because it's only single-threaded and I can call on eight threads, eventually I can kick its ass, right? Our 60 milliseconds is slow, and Mathematica, at seven seconds, it's not a fair comparison because it's doing everything symbolically. I would put Python in here, but I don't actually know Python well enough to put a benchmark up, and I would hate to be called out and have nasty things said about me on the Internet. So that's like, okay, the point of this slide is to say, okay, I can express the math cleanly and I can make it go quickly. So now let's take the next step. Let us look at... Excuse me one moment. How would I actually define that? That's like timings. So now here's my... I made a namespace called entropy, and I got the same thing in the entropy, and a function bits, native log function is base 10, so I don't need to convert all my answers to base 2 so they're expressed in bits, you can ignore that. And I've got two main functions, entropy, which is the entropy of a distribution, and that's using the same code I showed you earlier, using the reducers, and then, you know, I was talking only about language constructs and how important that is, you know. So a closure gives you pre and post conditions so I can easily mix my maths with some production type stuff, like pre and post conditions, and improve the quality of my code. I can define relative entropy. For giggles, I've done that using JBLAS, and it's the same thing. So I'm going to take the log p over q, and that's expressed very cleanly here. So you divide p by q, take the log and the dot product, sigma p log p log over q. So the way these functions work is they're taking a collection of numbers p and they calculate the density of that. So the density is just a map that says how many times each element occurred in p. All right, very simple. So here are my two functions. Right? The point of this slide was simply to show these things, to show that closure gives you some language features. Now let's connect you to some data. So, set up db. Funny you should... You expect me to use the atomic in my demonstration? Yeah. So I've got a very simple example here. So I've displayed some maths. I've displayed how to write code about it. Now let's make it real and connect you to some real data. So I've got a very simple problem here. Let's say I'm running a bunch of servers that produce responses. So I have a simple database. It's got one entity, which is a response. There's two attributes, which is... That's romantic. Um... I mean, a man who loves his work, but honestly, it's got two attributes, which is a server which produced the response and the time of the response in milliseconds. So it's the simplest thing I could do. I don't want to get trapped up in all the complexity of databases. So that's a very simple thing. And I've made some data and I've put it into the database. So let's go have a look at some queries over that. Hmm. Okay, so I connect to my database. See if that's working, yeah. Okay. That was a false, that's a bit worrying. And now let's ask some standard questions. So I have this data in my database and I want to see if there's something going wrong. So what I can do is I can grab all the responses. So this is a query. It says get me all the responses, the times, then aggregate up. That's kind of cool. So you can aggregate by server ID and the collection of response times that you get in pass into the average function. So if I do that, if you look at the bottom, that's the answer that comes out. So it says I've only got four servers so I could fit it nice into the mini buffer. Server one had a mean response time of 50, two of 50, three of 50 and four of 50. So the means of these things are pretty much the same. I can do the same trick and look at the variance. And they're all roughly the same as well, but here is I'm asking a closed question. I'm asking a very specific about these distributions. Are the means different or are the variances different? This is like highly specific. I'm assuming things about these distributions. And as you can expect, like if I ask relative entropy, that's a much more open question to ask because it doesn't assume anything about this stuff. So how would I do that? I'm going to take the relative entropy to the population. So I'm going to say, okay, sigma p log p over q, I'm comparing two distributions. P is my population. I'm going to take all of the responses that I've gotten in my database and then compare the responses from each individual server. I measure the relative entropy between the two of them. So in my earlier slide, the population is the red distribution at the top and the blue ones at the bottom are each individual response. I can work that out. So how do I express that enclosure? Well, here's the atomic query that gets all of the response times. And then I partially apply that to relative entropy. So relative entropy, you'll remember, perhaps this will even work. So my function has got two arguments and two distributions. P is my distribution over the response times of all the servers. So I've bound that in. And then I give it a name. So I just bind that to the function relative entropy. And now here's the lead. I can now take this function and pop it directly into my atomic query. So now the atomic has got my data locally. And I can pass this to my function using reducers or native JBLAST, whatever it is, and it just meshes together, integrates. And that's like unparalleled power. So let's have a look. Let's see what does it look like. Okay, so the difference from one, the entropy of one is 0.3, 2 is 0.3, 3 is 10, and 4 is 0.2. Do you think I've clicked these numbers so that it comes out this way? Right? So there's something on. Something's going on with server number 3. At this point I don't know what it is. But because I'm in an interactive environment, I can just pop up densities. So 3 is different. So let's look at what the distribution of 1, 2, and 4 are. So I get my query from my database. I just change its format slightly, calculate the density, and then view it. And it looks kind of like a normal distribution, right? So now let's see what... Ooh, let's see what number 3 looks like. This is the one that had the faulty answer. All right, so let's have a look. Let's change it up. I can interact with my data. This is the point. I can play, see things. And oh my, look at that. What question do I need to ask of data to say is this thing bimodal? Like, how would I even frame that question initially? Whereas something like relative entropy, it just pops out. This distribution doesn't look like the other distributions. And that's kind of like an awesome thing for data science, because that's the kind of questions you want to ask. Like, is something freaky going on? Like, what's interesting? What's different? And this is by asking relative entropy type questions. You can ask that very cleanly and openly. Okay, so that's my demonstration. And let's go back. Oh, let's go back, I said. Right. So, in summary, you can take a pretty abstract concept like relative entropy. You can express it cleanly in the mathematics enclosure. You can then get a handle to make it go quickly. You can put it on a platform, connect it up to data. You can take the data, you can interact with it. All in one platform. And that is like the power to rule the universe. Well, maybe, in my mind. Right. So, the ladder enclosure is slightly different to the other ones. It's got the same number of rungs, but unlike the rest, it goes all the way to the top. The trade-off is that the distance between the rungs are higher. So it's slightly harder to do each particular thing, but it gets you from all the way at the bottom to all the way at the top. What's missing? Like, native libraries, which I mean closure native libraries. We need a lot more work and stuff in there. You can get stuff on the JVM, but it's kind of ugly and non-idiomatic. So, that's the origin and statistics. Numerics, we have no hope. That's kind of a trick by using the multi-threadedness. Because we're on the JVM, we're never going to get native fast numerics. And that's just like something on the JVM you're going to have to deal with. It's just a trade-off that's been made. So what's the future? In the near term, more libraries and ports. Hopefully, that's going to be produced as people start to use this more and more. Session, which is a great library from Kovas. I'm not going to steal this thunder, but that's integrated with what I'm talking about is incredibly awesome. Encanto 2.0, I think Encanto could be like one of the killer apps on closure and it needs a bit of love at the moment, so I'm hoping that that'll pick up. In the medium term, what can be done? Probabilistic programming, kind of my bug there. So, probabilistic programming is, well, what logic programming is to deduction. Probabilistic programming is for inference. So instead of having variables that are logic variables, they're random variables, so they're drawn from sets and associated with them are probabilities. So each variable represents one of those distributions that I put up earlier, and you can then ask probabilistic operations of it, like to marginalize or condition. And these sorts of things, if you can express them cleanly in closure, would be a massively powerful tool. Now, there's an implementation that exists in scheme, of course, called church, which we could use a template for that. And I'm told that there are also things in C-cundrain and minicundrain that allow us to do this. So I think some attention and work there would yield massive dividends. So if this is also awesome and cool, like, who's actually doing it, right? Here's a company. It's called Zen Robotics. It's based in Finland. And what they do is they buy these robotic arms, just the hardware from Germans, and they fit them out with AI. So what they do is they sort recycling. So the robot has a camera on it, and the belt below goes by with recycling waste. And it separates out plastics and metals and all those sorts of things. And the whole thing is done in closure. There's 130,000 lines of closure in this company running these robots. That's, like, the biggest closure company no one's ever heard of. They've just raised 13 million euros to scale out. And I, for one, welcome my robotic overlords that are high on Lisp, you know? It's brilliant, right? But this is kind of, like, where we are at the moment with this closure ecosystem. It's like, for the wizard-level problems, it's a very, very compelling story. But for lower-level type stuff where libraries exist, we're not really there yet because people can easily reach out to something like Python and get the stuff done there. And if we make it easy to reach out to closure, we will enable them to make much higher and more difficult analyses possible. So finally, just a quick punt myself. This new collaboration between Christoph Grand, Michael Brandelmaier, Sam Aaron and myself, called Lambda Next, and we're going to be providing some closure training and consulting services in Europe. So I hope if that would be useful and helpful to you, you'll reach out to us. Thank you very much.