 You're going to hear me all right? So yeah, welcome to this lightning talk on metroprogramming in NIM. My name is Peter. You can find me on GitHub and IRC as P-Monk, or on my website as peterme.net. After this talk, I'm also going to upload a companion blog post. I only have 15 minutes here, and this is a big topic. So if you want more details, either about NIM or about metroprogramming in NIM, you can head over to peterme.net, and there will be an article there available soon. So I discovered NIM while I was doing my computer science degree in Tromsø, Norway. I've been using it mostly exclusively for about three years, but I don't assume that all of you actually know what NIM is. So I'm going to start with a short introduction to what is NIM? So NIM is a programming language. It's statically typed with a very good type system, sort of like the type system you'll find in Ada. So it compiles down to C and C++, but it's very optimized C and C++ and not something like the C you would write yourself, and also to JavaScript. So you can, in fact, do full stack development using just NIM. So when it was created, it was meant to be sort of a mix between the speed of C, the ease of Python, so you can see a syntax example on the right here, and the flexibility of Perl. And it's this flexibility component with the metroprogramming stuff that I'm going to talk about today in this talk. So what is metroprogramming? On the left here, you see something that is probably familiar. It's from C or C++, so many languages have some kind of metroprogramming capabilities. And metroprogramming is really just about letting the program or letting the programming language sort of rewrite itself, so programming the programming language. So on the left here, you can see simple stuff like on compile time, dependent on some variables, do something different. So changing how the program works with the program itself. On the right hand side, you can see the equivalent NIM code. And as you can see, all of NIM metroprogramming stuff is actually written in NIM, so you don't have to learn a new metroprogramming language to be able to do metroprogramming in NIM. But why do we want to do metroprogramming? I mean, the simple stuff like import this on this platform and import this on that platform is fine. But metroprogramming is much more than this. So we can use it to optimize our code. All of metroprogramming NIM runs during the compile time, so we can rewrite complicated things or rewrite stuff that looks simple to us into optimized and complicated things that the computer is able to understand. We can also enforce better coding patterns. So many times when we're writing programs, we have to stick to sort of a way of doing things. So you have to do stuff in a certain order or you have to remember to release a lock after you get the lock, stuff like that. And these patterns can be enforced by using them or by using metroprogramming. We can also increase both read and maintainability of our code simply because of the way that we can take something that looks simple to us, that's readable and sort of understandable to us and turn it into something more complex. So we don't have to worry about the complex part. We only have to see and maintain the easy part. So metroprogramming NIM, the example I showed you in the beginning with metroprogramming in C is all done with simple text substitution. So in C, it will take your inputs, it will read through the source code, do all the replacements and then pass it on to the compiler that actually does all the heavy lifting. In NIM, it works on the abstract syntax tree. So the compiler will actually read through your source code, create data structure, a tree data structure that represents your syntax. And then it's this syntax tree that we're working on as our inputs and outputs for our metroprogramming. And this has some crucial benefits. It also respects the type system. So we can actually do stuff dependent on different types. So in C, you would only be able to have sort of the same substitution no matter what you put in it. If the compiler complains about the stuff that the macro spit out and not what you put in, but we can have type checking and say that no, you can't call this macro because this wouldn't generate the right time on runtime. There are also sort of levels of complexity. So you have the normal procedures or functions as they are often erroneously called in other languages. Inline iterators, and those are sort of available in most languages. We have generic procedures and closure iterators. They are a bit less common, but still very common in many languages. And then the two last ones, which are sort of the maybe most interesting ones, which are templates and macros. And I'm not gonna talk about the two top tiers here because they are so common. I'm only gonna focus on templates and macros for the time I have left. So as I said, templates or any method programming in NIM uses this abstract syntax tree sort of way. So this is abstract syntax tree substitution. So this is an example of enforcing a coding pattern. So here we have a with lock template that simply takes in a lock object and any untyped, just any syntax tree. And it will acquire a lock, try to run the code that we pass it in, and then it will release the lock in this try finally pattern. That is sort of what you're supposed to do with locks. But many people don't because it's, yeah. Do you remember? Do you care? Is it really that important? But with this, it's super easy to do. And of course, with the type system, we can also enforce that, so we can hide our lock away behind some distinct type. And we can say, no, you can only call with lock. You can't acquire the lock without going through sort of the proper channel. Can we save the questions for the end? If that's, or we can take them afterwards, just, yeah. I'm short on time. So this is an optimization example. Here we have a debug template that simply just writes out some debug information if our log level is sufficient. But we also have an expensive debugging info. So maybe you want to gather a stack trace. Maybe you want to do some other more complicated stuff to get debugging information. But if your log level isn't sufficient enough, you're just gonna take that data in and throw it away. So with this, you get a sort of lazy evaluation because when we call debug, expensive debugging info, expensive debugging info is actually gonna be substituted into the args parameter in the body of code in the template. So it won't be called unless the log level is sufficient. And this means that we can, like even a simple example such as this can save us in this case a second of time every time we want to debug this. So, and this is of course, this is of course on runtime. So the macro is expanded on compile time, but we can change the log level on runtime. So it's not like in C where you can have, of course we can have a compile time switch to switch on and off debugging information, but then we can't ever turn that back on. This could be done on runtime and we would still have the same savings. So macros and these are sort of the most interesting part, saving the best for last. Again, it takes the abstract syntax tree as a data structure in memory. It also returns an abstract syntax tree. And because it takes a syntax tree, the input must of course be syntactically correct, but it doesn't have to be semantically correct. It doesn't have to be a valid NEM program, but it has to be something that the NEM compiler is able to parse. But NEM syntax is fairly flexible, so this rarely turns out to be an actual issue. And with this we can do a lot of interesting things. Amongst others create the way in specific languages. And this is an example from the standard library JSON module. So here we have a simple operator, a percentage star, which takes the input and converts that to a JSON object. And it doesn't convert it to a JSON string, it converts it to an actual representation, object representation of a JSON type. And you just end up typing JSON into your NEM file. Like why would you stick around with creating objects and inputting information and all of that, when you can just type your JSON. And this of course is rewritten on compile time into something that actually creates the objects and stuff. A more complicated example, which is actually one of my first projects using macros. This is the macro that generates a user interface for VX widgets. And here you can see I'm using the heretical structure of an indented language. So we have two static boxes. They are in a box size here that is horizontally laid out. The items are vertically laid out. We can see we have buttons, we have check boxes. And we don't really name anything. Like we name the gauge because we use it in the callback and we name the mainframe to be able to show it. And this spits out something horrible like this. And this is what you would normally write when you're writing, if you're writing like if you're programming your actual user interfaces. This is what you end up writing. And look at all these temporary variables. Like this would be button number 728 in your code. And you would have to be like, ah, is up, is that 728 or 729? I can never remember. But with this, we can bind directly in the macro and it does all of this horrible stuff for you. And just to show like this is what the actual output is. And we can see the same structure here. We have the two static boxes side by side. We have the two or the four ridges in them below each other. And this is interactive, like, well, this is a screenshot, but the program is interactive. So if I hit the button, you can see the gauge has already gone up to 50% because I clicked the button. So yeah, that is metaprogramming in NIM, or at least a small peek at it. Again, if you want to know more, you can head to my website and read the companion blog post. Or you can, of course, just Google NIM or go to nimlang.org, like nim-lang.org. Yeah, thank you. Yeah, anyone have any questions? Yeah? Yeah, so as I said, it compiles to C and C++. So using C and C++ in your NIM program is basically just saying I have the C function, call it. So this code is from the VXNIM project, which is just wrappings for the VXWidget C++ library. And this is just metaprogramming or a macro on top of that. So if you've used VXWidgets, you'll probably recognize all of these because these are just the C calls or C++ calls to those functions. What's the question over here? Yeah? Yeah, so when you're writing the macro yourself, it's very easy to output what the output of the macro is. You can see the code. Also, the debugging tools in NIM will tell you, so this comes from inside of this macro and this is the part of the macro that generated that line of code, which is failing. So it's actually surprisingly good.