 just in case anybody doesn't know which one the ring is, it's that one. We have the Dark Lord of the Sith, my boy North Vader, and we have macros, so just in case that's a macro, or that's the definition for using macro. So I'm going to give everybody a little quiz. It's very important. So what are these three things having in common? And fortunately, I know we all hate quizzes, I especially hate essay questions, so this is going to be multiple choices. Is it A, rule by evil dictator? We have unsavory characters up here, we have Sauron, that's Sauron, we have the Emperor, and we have Jose Malin. So that's A. Or is it B, incredibly powerful and dangerous but can still be used for good in the right place. Also some unsavory characters up here, and this little bevel cartoon character, which is the best I can find. Just to review our choices, because this is a pretty tough quiz, is it A, evil dictator, or B, powerful but dangerous? And I think we can pretty quickly roll out A. I think it's totally unfair. The Emperor actively trains apprentices, so my definition is not a dictator, so we've got to roll that out. And so that thing is just a B. Just kidding, just kidding. Everybody knows Jose is the nicest guy in programming, so don't go and tell him I said this, even though this will be on YouTube. Anyway. Yeah, B, powerful but dangerous. That's kind of going to be the theme of this talk. Why is it powerful? Well, we can do things with macros that we can do using regular functions. I have an opinionated, and technically these are opinions, but I think there's more or less facts, you can be the judge of that. So number one, macros are more complicated than functions. Number two, macros are harder to debug. And three, macros are harder to test. It also is kind of a fourth point, I know everybody gets sick of talking about Ruby at an electric conference, but I do Ruby from 9 to 5, and the absolute worst problems I've ever had to solve were when people were better programming unnecessarily. That's sort of a lot of my motivation for doing this talk, because I don't want that to seep over into the electric community. So with that being said, macros are an incredibly useful and powerful feature, and you should not be discouraged from using macros when it actually applies. So here's an example of when you might need to use a macro. Let's say I wanted to be able to take a mathematical expression and compute, or not compute, excuse me, log the steps that the literature takes to solve that problem. I couldn't do this with a regular function, because what's going to happen is I'm going to call analyze, which is a terrible name, don't name yourself I'm going to pass this expression into analyze, and this is going to equate to be 1.5 before it ever gets to analyze. So if we wanted this, well we can't really construct that from 1.5, that just doesn't work. But we can do that with macros, and it comes at a price, because like I said, macros are more difficult to understand, maintain, debug, and test. So here's what you would see if you were using a function. And here's what macros sees. If you haven't seen an AST before, this is what it looks like, and frankly this is a pretty basic one. So I don't think it's really debatable that 1.5 is easier to understand than this crazy nested tuple of rage that's the only way to describe it. Trying to debug that just makes me really sad. So like I said, this is an AST, and before we can really talk about macros, we need to know more about the AST. So in Kristen Kord's book, which is awesome by the way, Meta-Programming Literature, I suggest you read it if you have any interest in metaprogramming or writing a library. Hey, Meta-Programming Literature's purpose in life is to interact with the AST using the literature's high level syntax, and it stands for abstract, excuse me, stands for abstract syntax tree. And that's how Meta-Programming, we manipulate ASTs at runtime. And the cool thing about ASTs is they are written in electric, so it's really easy to work with them. We don't have to drop down to C or whatever language another AST might be written in. So let's look at an AST. Here I have this reckless session that I have open, and I have this function sum which doesn't actually exist, and it's taking the list of integers, and we can get any valid literature's AST representation by passing it into a quote block. We say quote do, and then it will spit out the AST. And what you get is a three-element tool where the first element is the function name or variable name or macro name. And the second item is a list of metadata, if clickable. And the third item is the arguments passed in from left-right. But some literature literals are already in their valid AST form. So some of the ones that will return themselves. You can see up here, integers, character lists, strings, lists, two element tools. And the ones that don't are three element tools, variables, maps, and macros. Additionally, anything that's not included in that list will continue to be expanded all the way out until you get down to something defined on kernel special forms, which is basically the building blocks of the literature. So that's a lot of clicking. So I bought a Targus clicker. I don't recommend it. Go with the logistic one, because this keeps happening, and so I'm pushing the button super gently, and I guess I have an APE or something because it just keeps going multiple times. Anyway, so now let's take a look at a macro. So the anatomy of the macro, they're always going to receive an AST and they're always going to return an AST, and you have two types of macros. You can define a regular macro, saying death macro, food, whatever you want to call it, and then you also have a special using macro, which you can define functions, you can find more macros, and every module that uses that module that has the using macro, everything inside of there will be evaluated in the context of that calling module, and we'll see an example of that later. So a lot of the examples that I'm going to give, actually all the examples that I'm going to give, I'm going to use the most basic macro that you can possibly get, which is a macro that doesn't do anything for the most part, and we'll see how complicated it gets when we're literally not doing anything. So it seems that the conical example and intro of the macros has been re-implementing the if statement, because if you didn't know, there is no if statement in Elixir, it's a part of the language, it's a macro. It turns out a lot of things in Elixir are just macros. So just to kind of point this out up here, online with Maria had a comment that says macros context. Up there, everything kind of behaves as we expect. There are, you can make functions, you can make variables, you can pretty much do whatever you want and things behave as expected. Down here in this quote block, that is going to be what the macro returns, and it will be basically injected into the caller's context, and that's where things start to get a little bit weird. So first we will just see if the things that I've said are holding true. Right now we're just going to inspect the condition and we're going to inspect the block requirement, our bar if macro, and we will call it down here in our rebel session and I'm only just passing in the string foo, we're not doing anything. So like I said before, AST is a 3L of tuple, we have an equals equals macro, it came in on line 4, and there's our arguments 1, 2, and our block is the string foo. So pretty straightforward, right? Not a big deal. But let's actually implement our macro because we can't really do a whole lot of useful things in the macro context. So what we do here is we unquote our condition, which if you remember is 1 equals equals 2, and basically what that's saying is, hey, we're returning AST in this quote block, but I don't want to use an AST right here, I want the actual evaluated value. So obviously 1 equals equals 2 is going to be false, so we're going to return something that's basically case false. So we all know it's going to happen there, result in false nil, it's going to return nil, otherwise we unquote the block, we inject that value back into our macro. By the way, there's going to be a lot of code in this and it's going to get a little bit more compact in this, so if you want to see the code feel free to move up, there's a lot of space. So this is pretty simple and not too much visible to grok, but it's not really all fun games, it only gets more difficult from here. Here is kind of what we have with the inspect block, we have our AST and we have our string. But all we're doing here is returning a string. I don't know what everybody does for your job, but my job rarely consists of returning strings. Usually we're doing something a little bit more interesting than that. But at the same time I want to keep this example simple. So let's sprinkle on the absolute minimum amount of complexity that I think is possible, and instead of returning that string, we will inspect it. So that blows up pretty quick. Still not super complicated, I think if we absolutely had to get through this, we could. So maybe I'm wrong, maybe backwards aren't evil, maybe this is just a little bit of negativity on my part. You know, dark side and evil and Frodo and all this, maybe I'm just full of it. So let's take a look at this. So we have three of them, two full. We're calling it dot, and we have a list with another list and aliases and more aliases in my module, and I don't even know, I can't. So, apparently Bader has something to say here. Let's see what he's got going on. I hate it when someone proves me wrong. Earlier I said we have our my if statement over here, and as we know the ASC has to be fully expanded and we saw that we implemented my if with a case statement. So this thing is not really fully expanded. So let's see what full expanded ASC looks like. So that got kind of out of control real quick. So imagine you're working with somebody's library or somebody's source code they're taking over and you come to a part of the code where somebody used a macro and they didn't need to, but now you have to debug this instead of the normal nice function that you could debug. So just kind of parse through this for a second and think about that. I get pissed. That's how I feel. I just, I feel the rage building up. It's like when I'm working on Ruby and somebody met a program for no reason and just dark side man, you go there. So that brings me, that is all the service of my first point. Macros are complicated, more complicated than functions. I think that that is not as beautiful because Elixir is never going to be less complex than its ASC representation. It can be equally as complex because some literals return themselves, but most of them do not. So let's move on. Macros are tougher to debug. This is how I feel after looking at a macro for a long time. I'm beaten and worn down and like I am totally being controlled by this horrible terrible thing. But before we can look at debugging we need to talk about macro hygiene for a second. So macros have the concept of hygiene which means that the context of the macro won't infect or contaminate the context of the caller unless we explicitly tell it to. So up here we have our module bar. We have two functions, hygienic and unhygienic. They both bind X to 1. They call hygienic foo and unhygienic foo respectively and then they return X. And we have our module foo which has two macros, unhygienic foo and hygienic foo. And you can see that hygienic foo simply binds X. And unhygienic foo has this far-bang notation and what this means is hey, go into the calling context, find a variable X and override that value. So this allows you to clobber that variable's value and this one will not. So if we log that to the console you will see that that's true. So now when we talk about debugging once again we're going to start off with a real simple example. We ran a module called basic math which we're actually not doing any math. I'm just going to keep with the passing in math theme and we're just going to inspect that ASD and we're going to drop into a prize session up here. Next slide, sorry. So we have our function down here we're going to bind X to the caller X and we are going to bind X to X in our acro and Y to Y. We're going to inspect those two things and we're going to drop into the debugger. So keep in mind that we have to try all my eight of this macro if that's kind of important. So we see it print the things as we would expect X is X, Y is Y and we drop into our pry. Now the weird thing here is that we don't see our pry. We see our pry where we call the macro. So that might throw you through for a loop you're debugging a little bit because usually when you drop into a prize session you expect to be exactly where the pry is. And I believe that happens I'm a little shaky on this I believe that's going to happen because ultimately that macro is only going to return the final AST and you can't actually you can't actually get into that during runtime I have to dig into that a little bit more. Anyway, so then some more weird things happened we printed our X and printed our Y from macros and now from our macro and then now we put Y to the console and we would expect to see the value for Y that we could find in our macro because that's where our pry is. Because our macro only returns the AST and at this point that variable Y does not exist. And then if we call X we have caller X which is what we would expect to see because we didn't override the variable X but then also you see the variable X was printed up there with the value X so we kind of have two different values for X depending on if it puts the bugger or a pry bugger that can be confusing. So not too much more difficult but you do it to keep some more things in mind and that brings me to my third point macros are harder to test and this is my opinion I don't think it's actually harder to write the code but I do think that it's more difficult to make it completely clear about what you're actually testing So who has a kid? Anybody have kids? I have kids. So in my younger developer days I thought I was going to be some rock star open source developer. I was going to be on some core team or just be some awesome dude who writes a bunch of cool stuff, right? Turns out that's really hard to do when your two-year-old is driving Tonka trucks on your face while you were trying to write code. So I decided that I was going to get back to the community by trying to speak. At some point I was like, well, I really want to get more to a lister so I need to find something to write and there was no Spotify API wrapper and I was like, well, API wrappers are easy like they're talking to be a whole bunch of maintenance once it gets up. Let me just do that quick. So I did that. So I wrote a library called Spotify EX which I was pleasantly surprised to see something we had on a slide earlier and so far all of our macros have been of the deaf macro sort but we're going to take a look at one of the using versions and I'm going to use an actual example that full out of my Spotify library for that. So like I said earlier, using will take everything inside the using block and inject that into the calling module and just for some context where I'm using it is I have a module called responder which each of my modules in Spotify EX can use that to handle HTTP responses and then pass that off to some kind of function that defines how that function should handle your response. So like in this example I have a Spotify artist that uses the responder so if basically if there's a HTTP code with an error of 400 it logs the error if there's a 200 response where you have an empty body it just says okay and then if there's a response body then now it will rely on a callback implemented by a Spotify artist to determine how to handle that response because it needs to take that JSON and convert it into a list of structs or a struct or whatever that data needs. So there's an example so like here's an endpoint we get an artist, we get our URL we pass that into our client and then we handle response and here's the responder model and one of the interesting things about this is right here you'll see a function called build response this is the entire module this build response is not defined here so you would think that that compiler would kind of throw up when you did that but it won't because Elixir knows that everything inside of this macro is going to be evaluated inside of another macro it's not another macro excuse me everything inside of this macro will be evaluated in another module so like I said handle response 229 otherwise message otherwise build a response back so the problem I have here is I wanted to test these functions and it's a little bit more tricky it's a little bit trickier to test these because you can't just get into a price session and call responder and handle response it has to be inside of a different context so it was kind of an interesting question that I had to answer when I decided to test these and truthfully I'm not really having with my test for these so I'll share some of my shame with you guys and I have two different ideas for how to test these and I would love to hear any opinions about which one you think is the right one like I said a lot of code for this part but you don't really have to see it anyway so this part right here is actually our test and I'm describing handle response and I'm testing each of these functions but I have to provide some kind of context for these for our macro to be included in so I built a fake playlist API here I'm using our responder module and I just stub out the things I need to be stubbed out and now I can call fake playlist API at some end point and I can assert that handle response is doing the right thing but it's not very clear because I'm saying describe handle response but I'm calling some end point so the test works fine but when somebody jumps into this because maybe they want to make a PR or fix a bug it's going to take them a second to grok this and maybe a year later or whatever when I go look at this I might have to take a second to grok this so that's option one and option two was well I can just use responder in my test but that feels really weird too because now what's responder is it a test library is it some thing that gives me some magic testing abilities I don't know it's kind of nice because now I can call handle response directly because we're using responder here but it just feels wrong in my opinion your test should not be the context that you're that you're testing a macro in so I went with the first option I would love to hear opinions about it so tweet at me, solve me the ball yell at me, whatever I would love to hear opinions so so far that's been a lot of negativity about macros but I want to reiterate that macros are awesome because they let you do stuff that you can't do with functions so when should you actually use macros my opinion is that you use macros when you have to manipulate your ASD in order to do the thing you want to do if not it's probably your best bet so going back to the beginning of the talk we had our analyze math function that took a expression and it printed out the steps that Alex should go through to solve that in order and here's what the code might look like or something like that and this is just a quick and dirty example it could probably be done better but I just kind of whip this up real quick there's a lot of cool functions that you can use with macros like macro post walk and pre walk is an example so post walk will let you walk the ASD depth first and pre walk will let you walk the ASD breath first and in this case if I post walk it goes through the steps to solving them in reverse so I can post walk that stuff all that into an agent and then basically just reverse that and pop the stack and I can manipulate my ASD down here as I go and I can print the steps so there's no way I can do that with a function because a function would have just gotten 1.5 and you can't do anything with 1.5 so what else should you use macros when you need to inject code into multiple places and it requires not doing the trick for whatever reason or you really want to have a DSL because you're building a library or whatever reason there's no hard and fast rules provided to writing DSLs you have to make that call but I think that you should avoid it unless it's really necessary so the the moral of the talk is be good and not evil typos jeez unless when you need to be evil that's those last minute conference jitters where you're changing your slides right before you're about to get up there and you make an awesome learning typo so far this has been a lot of doing with lube, macros are bad but we've also seen how powerful they are and as literature developers we have access to that power at all times and it's up to us to use it when it makes sense so going back to our Star Wars sort of theme that we had the whole time we're like oh lude come to the dark side blah blah blah come be bad for me we'll take over the world for universe or galaxy or whatever and luke's basically like nah dad look good I'm just going to do my thing and finally he was like well I really got to take out my dad because he's going to get my sister and she's going to be corrupted so I don't know if you've seen the newer Star Wars where they have these ruin all again lightsaber battles and what we want ultimately wins by taking the high ground so that magically makes him the winner well lude didn't really do that he just basically got really pissed and beat the hell out of her so that's kind of like in my opinion that's like tapping the dark side I mean he just beat the shit out of this dude but when he was done he stopped and he went back to being good so he used it when he needed to and then he stopped and if we look at what happened in Lore of the Rings well our hero Frodo he kind of used the ring all the time and he corrupted him and at the end of it he pretty much got lucky that the weird little creepy golem creature jumped into the fire with the ring because he wasn't going to destroy it so be like Luke don't be like Frodo only use it when you need to and if you do you'll be really happy that you did because you want to debug that macro but you're down the road or anywhere anytime in the future that's all I got so I'm going to take questions outside because I know it's the end of the day and I'm sure a lot of people want to be in any traffic or just get out of here thank you