Anda di halaman 1dari 4

[music] Now that we've seen two ways to define our little arithmetic language, and write a function

that evaluates expressions, I want to emphasize the reasons why I think the approach with Structs is a superior one, and it's good to have support for that sort of thing in your programming language. So the idea here is to really contrast the approach where for each kind of expression we just had one struct definition and then we got a bunch of functions for testing and accessing and building that kind of expression versus the first approach where we just did it ourselves, ourselves using lists. So we created lists for the first element of the list said, this is an add expression and then we used later list elements for the sub expressions. And the thing I want to emphasize is that struct definitions are not syntactic sugar for the list approach. So the key reason they're not, is that when you call the add constructor to build a new add expression with the Struct approach, you do not get a list back. So let me convince you of that. Here's the same file we had in the previous segment we were using Structs. And if I define x to be add of const 3, and const 4, that's a perfectly good add if I prints like that. It is not a pair, it is not a list, it is not a mult. Sorry multiply that's what we call that other constructor. It is an add. Okay. So that's the key difference and what is really going on is that it's like each struct definition is taking racket and adding a new kind of primitive data which is the add expression, so the same way that a cons is a pair. And a number is a number. Add makes things that are adds. Now of course Racket doesn't know that these fields need to be anything in particular. We could also have and add just made out of 7 and 19. That's not correct in terms of how we wish to use add, but it is in terms of add being a new kind of thing, data that racket will allow us to, To make. Okay? So that is that approach. This is nice.

Because if you try to use the wrong accessor functions for the, for something, you get an error. And that's good, so that you don't silently do the wrong thing. So, again, x is just some ad. If I tried to, say. Treated as a multiply and get the e1 field, I get an error. So, by using struct definitions, it helps keep my add and my multiplies, as well as everything else, separate. I can't take cdr of x either. There, okay. So I think I've made that point enough. The list approach does not have any of these advantages, because all the things we're making with the add function, which is just a plain old Racket function that returns a 3 element list. Are list, they are consoles so you can do all sorts of things in error that you might not expect to do. So you see some on the slides how about I show you some in the code as well. So now I'm just going over to the approach from two segments ago where we defined helper functions for everything. I've used capital letters here just to keep it straight in our minds. But I could define this x to be add of Const 3 Const 4. Okay? It's a list and if I ask is it a list, the answer is yes. And that's fine but suppose that I wanted to do what I should do with the e1 function of x and get that Const 3 out. Well, I could, instead, take the car of the cdr of x and there's nothing stop me, but worse, if I screw up and take the car of the car, thinking I want the car of the cdr, I just get sorry a cdr of the car or something. I just, you know, I shouldn't be thinking about this, I shouldn't be thinking in terms of const cells. I should be thinking in terms of adds and multiplies. And it's even worse because you would think in your head that car in a multipy-e1 function on x would be an error but it's not. You just get the const 3. And that's because if you actually look up here at the add function and the multiply function, the Add-e1 function, and the Multiply-e1 function, they do exactly the same thing. They both take the car of the cdr of their

argument, and so you can freely mix one with the other, and you'll get a lot less, sort of, timely error checking when you program in this way, 'kay? So, to summarize the advantages of the Struct approach, it's better style and it's more concise. Certainly, that's probably the first thing you thought of is, you just have one line with your struct definition and you get 5 functions for free. They are about equally convenient when using data types correctly. Even with the list approach, once you've defined all the functions you need, if you use them correctly it is about as convenient as the struct approach to use all of those functions. But if you misuse them and programmers always make mistakes you get much quicker error messages, you get failures sooner from the Struct approach. And that often makes things easier to debug. I should add that with a couple features of Racket that we're not going to emphasize so much, Structs are even better. The first is Racket has a very nice module system just like ML has a module system, and in ML with our module system we hid things By using abstract types. Now racket is a dynamically typed language, so sometimes people wrongly thing that in a dynamically typed language it's not possible to do type abstraction like we did in m l. It turns out you can because of how structs work. If we put a struct inside a module, and then only provide to clients of that module the accessor functions. But not the constructor function. Then they won't be able to construct anything without going through our sort of functions, that enforce invariance, and do the right sort of thing. Whereas, if your data is represented with lists, you can't keep anyone in your program from building any list that you want. It could just create a list who's first element is the symbol add and it will appear like it's an add expression even though it might not have the proper invariants. Second racket has something called a contract system where you can put on functions and data types like Struct definitions properties that have to hold

so you get even sooner errors and those properties can be user defined things, such as the subexpressions of the thing in an add have to also be expressions. That's not something I've shown you how to do here but it's something you can do with Structs. Then again, with lists it wouldn't make any sense because it's the same, you know, you would not put such a contract on all the lists in your program. You only want them for this new kind of data that you've specifically defined. I should finish with one other thing, which is, to emphasize that Structs really are a new thing we've added to racket. They're not something that you can code up with other features. A function in Racket can't introduce multiple bindings like we just saw. And even macros which I showed you a little bit earlier can't create a new kind of data. What really makes Struct special Is this fact that when you make something out of a Struct, it answers false to all the other types of data in the program? It's not a number. It's not a pair. It's not anything else. That's a key feature that programming languages can give you. But only via a built-in construct. If you only build up new data out of existing kinds of data then the things like number, or pair, or something has the answer true, because you're building it out of some kind of data that already exists in your language. And so those are the advantages of structs and why I find them a wonderful addition to a language like Racket.

Anda mungkin juga menyukai