of the course. And I just wanted to wrap up with a couple optional videos rather than end on a technical topic. As important as I think, bounded polymorphism is, after ten weeks and all the hard work you've put in, I think it's helpful to take a step back and review some of the big ideas in the course. I'd like to think of this as a victory lap. This is the extra run around the track that someone makes after they win a race, like a one mile race or 1600 meters or something. And it's just an opportunity to sort of review some of the things we've seen. Not at the sort of detailed level, like when you're studying for the exam, but at the sort of level of stuff. I hope you'll always remember some of the key concepts and themes that I think got into a course on programming languages. So, there's nothing new here. This will not particularly help you study for the final, to be honest. But, it's one last chance for me to say it and sort of finish up the course. And then, in the next segment, I'll have a few things to say about the online course experience itself, and all the people that helped make this course possible. So, here we are on the programming language content. So, let's start with what I actually had, believe it or not. And you may have actually read it in the about the course page on the course website. I hoped and claimed that if you successfully completed this course, you would deeply understand what functional and object-oriented programming are really about. I told you that you would be able to learn new programming languages quickly. I told you that you'd be able to master a number of specific language concepts. And we've certainly seen many in the various sections of the course. I told you, you would be able to evaluate the power and elegance of programming languages. And I even claimed that you would become more proficient in programming languages that you already know, and that we did not actually use in this course.
Now, only you can decide how successful
we were on these goals. I think the course in the end focused on some of them more than others. But, I think these are still good goals that things we should have aspired to. And I think the course content is hopeful in these regards. No course is perfect for everyone, but these were the goals when we started and they're still our goals now and I hope that we met them. here's a slide that I literally copied from one of the optional lectures in the course motivations that I hope makes some more sense now than back in the middle of the course when I posted it. And I pointed out that there's this very simple 2 by 2 grid of functional versus object-oriented languages and dynamically typed versus statically typed languages. And how I picked the three languages that we used as vehicles for learning programming languages. Because they cover three of these four categories. And many of you, before taking the course, had seen a language already in the 4th category. And if you haven't, I encourage you to pick one up now. You'll see that it combines a lot of ideas from static typing, like in ML. And from object oriented, like in Ruby. I told you that I picked these particular languages because of features I wanted to study. Whether it was polymorphic types, which many people now call generics, or pattern matching or macros or mix ins. There's just a number of things I wanted to show you that I could have taught in any language. But these languages do particularly well. And so, I use them for those purposes. I also want to emphasize, as I did in that video, that this is not at all a complete study of programming languages . There are several more topics we could have covered. There's more advanced material. There's more formal material. And particularly, there's two languages Haskell and Prolog, that either those languages or similar languages I think, would be an elegant, more complete story of sort of the range of ways to think about software. here's some slides I haven't shown you before.
I thought it would be neat to go back and
just look at some of the places where I pointed out reasons why mutation assignment statements are not as good as beginning programmers often think they are. So, I may have missed a few. But I noticed that all the way back in Section 1, we realized that once you give up on mutating data, it never matters whether two things are aliases for the same data, or not. Whether they're copies, or actually refer to the same object. And that frees your limited mental capacity to focus on other things. In Section 4 when we studied equivalence, we saw that if functions can't update state. Then, a lot more functions end up being equivalent. So, you can replace one with the other without worrying about it breaking clients. In Section 5, when we started studying Racket we saw that if you, that you don't have to make local copies of data. If you're worried that someone else might change something, it's impossible for them to change it. So, it's a little similar to number one, but it's more in the context of when we were learning about let and top-level defines and things like that. And then, most recently, near the end of the course when we were studying subtyping, we learned that you can have more sound subtyping if you have less mutation because they're in an inherent trade-off. So, as long as we're wrapping up this subject, I'm not against all mutation. I understand that the notion of state. And updating that state to a new value is often exactly the way to model the sort of computation that you're trying to perform. But, it's often not. And what I tried to do is sort of an antidote to the way programming is often taught, is to focus on state only when it's the direct. And immediate way to think about what's going on and not as a way to code up something at a higher level can be thought of as a function with an input and output. Or an object with method arguments and method result. So, that's the no mutation story that is
certainly not the only thing we studied,
right? So, I have some more highlights here that I just wanted to bring up one more time. Kind of the top several things from the course. First of all, perhaps, not a surprise to you, I still really like function closures. They're just a really powerful and convenient way to express reusable code by writing a function that takes another function as an argument. We even saw how to implement them, and that they're not magic, and that lexical scope can be implemented in literally in four lines of code. and, and as a result, we get all the powerful idioms that come from function closures and lexical scope. We also saw data types and pattern matching, which is something you tend to only see in functional languages. Although they are becoming more popular, particularly in languages that combine different programming styles. And we saw how sort of breaking functions down into different cases for each part of a data type is exactly the opposite of object-orientated programming decomposition. And I love the idea that with two things are exact opposite, you can really like both of them just as much. You know, rows versus columns, as I put it, when we learned that is really just a matter of perspective in code layout. I like that we studied a lot static typing. That we saw the benefits and limitations of static typing. I love catching bugs early. But it's also just that deep mathematical theorem that you cannot catch exactly the buggy programs by performing a static analysis, a compile time analysis, without actually running the code. We saw subtyping in generics at the end of the course. And how they're complementary to each other, and can actually be combined. And that's actually a great example of how often in programming languages, we add three or four features. And while those features are great by themselves, it's actually how you combine them that make them even better. We studied modularity, in particular, with ML's module system. I love the idea of hiding the
representation of a type definition.
I love the idea of private functions or private methods. These are things that come up in almost every programming language. I have a few more. I love that, thanks to this course, you can now think of programs themselves as data structures. That when you implemented a programming language, you represented programs in that language as trees, as those abstracts syntax trees. And once you realize those programs are trees, you can usually implement an interpreter for that language using an elegant recursive program. And that can change not only how you might implement a small programming language, but how you think about your programs being run after you write them. I this next one, let's go back to the very beginning. Hopefully, now every time you learn a programming language construct, I hope I could sort of get you to remember, gee, I bet there are three things I need to learn. What's the syntax for this construct, what are the type checking rules and what are the evaluation rules. If those are the first three questions you ask, you'll probably be in pretty good shape. And finally, there was this real point in the course of taking a lot of small things. Whether it was constructors or closures or objects or mix ins, and giving them a precise semantic. A rigorous definition of exactly what they mean. And then, letting them combine in elegant ways. And that you can use these constructs to write any software you want. And what I actually find truly awe inspiring about our world today, is that all of our software is built from a surprisingly small number of constructs. That we take these beautiful ideas with these precise semantics, and then we combine that with human creativity and ingenuity on what we want to build. And that is software, and that's what I want to say about programming languages. I hope you've enjoyed the course and I hope you watch the next video where I talk a little bit about the course rather than just about the content in it.