This course is an introduction to the basic concepts of programming languages, with a strong emphasis on functional programming. The course uses the languages ML, Racket, and Ruby as vehicles for teaching the concepts, but the real intent is to teach enough about how any language “fits together” to make you more effective programming in any language -- and in learning new ones.
This course is neither particularly theoretical nor just about programming specifics -- it will give you a framework for understanding how to use language constructs effectively and how to design correct and elegant programs. By using different languages, you will learn to think more deeply than in terms of the particular syntax of one language. The emphasis on functional programming is essential for learning how to write robust, reusable, composable, and elegant programs. Indeed, many of the most important ideas in modern languages have their roots in functional programming. Get ready to learn a fresh and beautiful way to look at software and how to have fun building it.
The course assumes some prior experience with programming, as described in more detail in the first module.
The course is divided into three Coursera courses: Part A, Part B, and Part C. As explained in more detail in the first module of Part A, the overall course is a substantial amount of challenging material, so the three-part format provides two intermediate milestones and opportunities for a pause before continuing. The three parts are designed to be completed in order and set up to motivate you to continue through to the end of Part C. The three parts are not quite equal in length: Part A is almost as substantial as Part B and Part C combined.
Week 1 of Part A has a more detailed list of topics for all three parts of the course, but it is expected that most course participants will not (yet!) know what all these topics mean.

AL

Great course!\n\nI think this course has just the right balance of theoretical background, formal definitions, and actual examples to make "just right".\n\nThanks Dan, and everybody else involved!

SK

Mar 24, 2017

Filled StarFilled StarFilled StarFilled StarFilled Star

An excellent course! Make sure you really have enough time to take this course. There are a lot of videos, but they worth watching. I'd recommend this course to everyone involved in programming.

À partir de la leçon

Section 3 and Homework 3 -- and Course Motivation

This section is all about higher-order functions -- the feature that gives functional programming much of its expressiveness and elegance -- and its name! As usual, the first reading below introduces you to the section, but it will make more sense once you dive in to the lectures.
Also be sure not to miss the material on course motivation that we have put in a "lesson" between the other videos for this week and the homework assignment. The material is "optional" in the sense that it is not needed for the homeworks or next week's exam, but it is still very highly encouraged to better understand why the course (including Parts B and C) covers what it does and, hopefully, will change the way you look at software forever.

Enseigné par

Dan Grossman

Professor

Transcription

In this segment I'm going to show you a new language construct. ML has anonymous functions, these are functions you can define without using a fun binding. But to do that, I want to motivate why we would want such a thing. They work very well, they're great style, they're more convenient usually when using higher order functions. So I'm gonna sneak up on this a little bit and show you a few versions before I actually get to anonymous functions. I have a similar example we've been using in the past to have this function n_times we're now very familiar with and then I have a use of it, this function triple_n_times that just wants to take n and x and return three to the n times x. So it calls its bodies just n_times with this helper function triple n and x. And this segment is really all about that helper function. And the first thing, I hope at least a few of you noticed, is that it doesn't make a lot of sense to define this helper function up at top level. This is really only a helper function for triple_n_times. So, it would be better style unless we needed a triple somewhere else in our program to define it as a local helper function like this and that's the second version I want to show you. Okay. So this second version is better style than the first version but now we might go even further and we might say, you know, triple isn't really needed in this entire body. If we want to give things the smallest scope they need we only need triple right in there. Okay. So let's try that. This is going to look a little silly but I promise you it's going to work just fine. Okay. What I'm going to do is right here in the first argument to n_times, I'm going to put the entire let expression. Now let's just make sure we understand what's going on here. I have a call to triple n_times, I have a function I'm defining triple n times, this is its body. It's a call to n*, n* takes three arguments. The first argument, is the result of this led expression. The second argument, is n. The third argument is x. So how do we evaluate this let expression? What we do is we define a local function, triple, and then right here we return from the let expression, the result of the lead expression, is that function we define and so that's what we end up passing to n*. This works great and it actually indicates fairly nicely that the only place we need this function is right here. All we're doing with it is passing it to n*. So this works. This is not good style but it's only because ML has a construct that I've never shown you that I'm about to and that is better than using a let expression in this strange way. So I really like the function is only to find right where we need it. I just have a better construct for it and that is to not give it a name at all. So one thing you might think you could do would be something like this. Right. That looks great, I want to call n* with this function defined right here n and X. Who needs a let expression just to return the variable? And this is the right idea but this will not compile and the reason why it won't compile is this is a binding not an expression. What we need is some expression that represents a function that is a function as an expression not as a fun binding. So it's a little bit different syntax but the idea is just right here. So here's the difference in syntax, the key word is f-n not f-u-n, so you get rid of the U. We do not write a name for the function, after all, we have this name triple, we never used it. All we did was define a function and pass it to n*. In n* it's called f. So if we don't need a name for the function let's not give it one and with f_n bindings, these function expressions, excuse me, they're not bindings they're expressions you cannot give a name. These are for defining anonymous functions. It's actually a rather clever name. Anonymous is an English word for does not have a name or is has an unknown name. Instead of running equals, we write this double arrow. It's just syntax, that was the choice of the people who designed them now and then the body and for some clarity here, how about I put this in parentheses and now this is the version I like the most. We now have an anonymous function defined right here. This is the function that takes an argument X. X is not the name of the function, it does not have a name. It's the argument and it returns 3*X. That's exactly what I want. I want the body of triple n* to be a call to n* with this function as the first argument, n is the second, X is the third. And so that is us sneaking up on anonymous functions and seeing what they're really great for which is when you are using first class functions and you're only ever going to use this function in one place, right here, so why bother giving it a name, let's just define it right where we need it. Okay. So, let's go back to the slides and see that progression one more time and then talk a little bit more about the difference between these anonymous functions and the bindings we've been using previously. Okay. So we started with this version where helper function triple was to find the top level. I said a much better way would be to at least put it inside, the definition of the function, inside the body of triple n* and then I said we don't need it for the entire body of triple n*. We only need it right in that first position. So I move the function definition to exactly where I needed it. Then I showed you that you cannot just get rid of the let. You cannot put a function binding here. You have to put an expression, let expressions are expressions, function bindings or not, but what we do now have new in this segment is the expression form of an anonymous function and that's what we finally saw in that last version I liked the most. The syntax is very simple you write f_n, you write the argument which can be a pattern in general.Here we just have one variable for the one argument, a double arrow if you will, equal greater than sign and then the body of the function. So is a function that takes one argument and returns three times that argument. There is no name for the function, just the argument. Okay. So the most common use of anonymous functions is when we're passing an argument to a higher order function. There's no reason to give it a name if this is the only place we're going to use it but the one thing you cannot do with anonymous functions is to find recursive functions. If you need a recursive function then you have to use a function binding with either a let expression or at top level. And that's exactly because the way we define recursive functions in ML is by calling the function itself using the name of the function and if our function doesn't have a name then we don't have a way to call it recursively. So, that's pretty interesting. Anonymous functions do everything other function bindings do except recursion and that is interesting because if it were not for recursion then fun bindings would actually be syntactic sugar. Let's look back again at a very simple function like fun triple X equals 3*X. We could have, even at top level, define that as val triple. I just want to introduce a variable triple and what that variable will be bound to is this function that I've defined via an anonymous function. That makes perfect sense and we could think of this first function binding as syntactic sugar for the second variable binding as long as triple is not recursive. If the body of triple itself caused triple then this unsugared version won't work because we won't have the name to do the recursive call. And, in fact, over here in the code I showed you I have another example of this that I can write out real quickly. I could actually write triple n* the same way. I could make this a function that takes in one argument that matches the pattern n, X and then calls n* with fun y, 3*Y and n-X now this de-sugaring is poor style. Okay. There are reasons not to do this. It's much easier to read the function binding version. They happen to mean the same things since triple n* is not recursive but most people find it easier to read the function binding, which is actually shorter, the same way that we use and also and or else instead of if then else even though we don't need to. Anyway, the high level takeaway point is if you only need a function in one place, an anonymous function is usually the way to go. Just like we don't make up variable names for intermediate computations that we only need everyone, only need once, we don't need to come up with names for functions that we're only going to use in one place.