, We're now going to move on to a different but slightly related topic which is macros. The way this is going to work is in this segment I just want to give you the high-level ideas of what a macro is so that in the next section, when we use that idea For a few things you're familiar with the idea. Do not need it for this sections homework, unless you attempt to do the challenge problem. So the way this is going to work is that after this segment I will go into the details of Racket's macro system. Some of the general pitfalls of macros, using this in Racket is actually a great idea because Racket's macro system avoids a lot of the pitfalls and weaknesses of other macro systems, particularly the more well-known macros of C and CC++, but I've decided to make all the segments after this one optional, because macros are a bit of a standalone topic, and we're not really going to build on them very much in the course. So it's sort of up to you, and this is an opportunity to save time and focus on other topics that fit more into the other core material in the class. You do need to know a few things, so let's go through that. And let's start with just what is a macro? So when you define a macro, it describes how to transform some new syntax into different syntax in the source language. I basically like to think of a macro definition as adding more syntactic sugar to the language. And if we let programmers define their own macros, then they get to extend the syntax of the language by introducing new syntactic sugar. For example, maybe we could have something that lets you add the andalso keyword to Racket in some way. And that, that would turn into the conditional that we know, andalso in ML, is syntactic sugar for. a macro system is just a language you give programmers for defining macros. And then what happens, is after someone has defined a macro, then someone can use that macro just like you define a function and then you use it. When a macro is used that macro is expanded, you take the syntax at the use, and you transform it into the de-sugared version, according to the rules in the macro definition. And the key thing about the way macro systems work, is that expansion happens before anything else we've talked about in this class. You take the program the programmer wrote down, you go through and you expand all the macro uses first, before you type check, if you're in a statically typed language, before you evaluate anything. So you do macro expansion in function bodies, you do it in conditional branches. It really is a pre-pass before you do anything else, and that's the idea of macros. So in Racket, just to get a little more specific, so we can actually see some examples, if someone defines a macro m, then m just becomes a new special form. So after that macro definition, someone can use it. By writing m and then whatever other arguments that macro needs and then, due to macro expansion, that will get replaced as though it's syntactic sugar, So here's a few examples, then the later optional sections we'll learn how to define. Here I just want to show how you might use them. So first, suppose someone didn't like Racket's if, because you know, they, they didn't understand which one was the then and this is the else. They could define their own macro, where you write my-if, then some expression, then other keywords, then and else, with e2 and e3. So e1, e2, and e3 are expressions. my-if, then and else are all part of the syntax, and macro expansion would expand that to if e1, e2, e3. Okay. That seems somewhat useful, not too exciting. Here's one that's also maybe not too exciting. You could write a macro, comment-out, that took two pieces of syntax, two arbitrary expressions, and in the expansion, got rid of e1. So the idea is that we want to comment-out e1, but we need to put something in its place, and so that will be e2. So it doesn't, because this is done before anything else we do, that e1 will never be evaluated. As a final example to relate back to something we did earlie r in this section, we could define a macrel for creating promises, somebody of my-delay, that would take an expression and by putting it under a lambda in the macro expansion, will make sure it does not get evaluated. And that's something that you simply can't do, if you try to define my-delay as a function. So let's see how we might do this. I've already clicked run, where I have definitions of all these things. And I really did define them. So for example, I could define, I could use the my-if macro. This is not something in Racket, I defined a macro that let me do this, and, you know, 7 actually comes out to 7. And you even get an error message if you screw something up, like write else in both places. It says bad syntax, the definition of the macro doesn't allow you to put an else in that position. let me show you the comment-out macro. if I did something like comment-out, how about car of null? We know car of null will raise an error as soon as you try to do it, but because macro expansion replaces this entire thing with false, I get no error and I just get false. We, you know, just to be clear, if I had actually tried to do car of null, I would get an error. And finally, let's look at my delay a little bit. So, let's say, P of my delay, begin print Hi and then about we multiply 3 and 4. So if my-delay were a function, we know that this argument to it will get evaluated right away, and it would print hi once. That's how functions work. But our my-delay macro picks up this syntax and puts it under a lambda, makes a thunk out of it and so there's no printing. Now we also have our old my-force function. my-force is just a function, it's right here, it works exactly like we learned in an earlier segment. And that we can now still use on the result of my delay, because that did return a promise. And now we see it print out and also return the result, 12. And because of how promises work and how my-force is implemented, we know that, that expression is only evaluated once. That we did a set m cdr bang to change the results, so if we force that same promise again, we get the 12 but we don't see Printing. So those are example uses of macros you can learn how to define in the upcoming segments. you know, and basically, it's likely added new keywords to our language. I should mention that macros have a bad reputation generally in computer science and software development, and frankly, they often deserve it. macros are often overused, or they're often used in positions where functions make a lot more sense stylistically, in terms of what you're trying to do. And I actually do want to leave you with the message of, when in doubt, if you're not sure a macro is useful, probably you shouldn't define one, and probably you shouldn't use one. But if you like that version of my-delay, where you just got to write e, and the user didn't have to write lambda parenthesis, parenthesis e, well then you really need a macro. Because no function in the world can have an argument that it does not evaluate. So since macros can be used well, I hope that the optional segments coming up can help people understand why macros are difficult to use well, and give some guidance on doing so. And so the optional stuff ahead is that we're going to talk about how macro systems need some basic semantics of how they deal with parentheses and variables and things like that. We'll learn how to define macros, like the ones we just used in this segment. We'll learn that when you're defining a macro you have to be extra careful about which expressions evaluate where and how many times. And then we'll see the key thing that Racket does better than most macro systems. Which is that it actually has a reasonable semantics, when macros define local variables, or use variables that are in scope where the macro's defined. This is something that most languages just do, what I would say wrong, but in some sense they're just a different semantics, and they work differently. And I'll show you why Racket symantics, is, is a sup erior one in almost all circumstances.