I hope you've learned to evaluate the power and
elegance of programming languages, even recognize sort of which tools,
which constructs are appropriate for different tasks you might be using or
at least have additional insight into how to go about doing that.
And I hope that you've not only become a quote, unquote better programmer in ML,
Racket and Ruby, but in every programming language you ever have used or
ever will use.
As I've emphasized over and
over again, programming languages is not about ML Racket and
Ruby, that's just the setting in which we've learned far more universal concepts.
So in terms of the actual outline, if you break things down into ten sections,
this is where you've now been.
It's amazing how much content you can have behind these short phrases that
describe these ideas.
So, you've seen functional programming and pattern matching in ML style type systems.
You've seen the dynamic typing of Ruby.
You've implemented your own programming language.
You compared object to any programming, to functional programming.
You've learned how to decompose programs under both approaches.
You've seen subtyping and advanced type system concept.
So, a whole lot of stuff.
And obviously, this is just a very brief summary.
Let me now for probably the third or fourth time show you this slide,
which is the very simple high-level grid of how we've covered three out of four
possibilities of combining functional or
object-oriented with dynamically typed or statically typed and
that's a good complement to a lot of people's prior knowledge.
Or if not, then future knowledge of a statically typed object-oriented
programming language like Java or C#.
And so that's been our framework is doing three of these four quadrants,
but I want to emphasize that it is not the case that all programming languages can
be described by being in one or even more than one of these quadrants.
There are additional things.
So for example, Haskell is a statically type functional language.
But its laziness, the way it delays evaluation of almost everything
in the language makes it quite different from ML and
then there are additional rows of this grid.
The most commonly known one that's neither functional nor object-oriented,
nor sort of lower-level and procedural like C would be prolog and
related languages, which are logic programming languages.
So we could do more, but I feel that where we are here at the end of part C tells
a complete story that covers a lot of the space of programming languages,
but certainly not all of it.
So I want to emphasize a couple more highlights of
what we've gone through things that I like to emphasize and the first,
perhaps no surprise is emphasizing the benefits of not mutating data.
A programming without side effects and assignments statements, and
imperative updates.
So I thought I'd point out that while that's by no means all of that we covered
in this course, it did come up several times and
I think it's worth pointing that out and I may have missed a few here.
All the way back in the first section of the course,
I pointed out that if you don't update things, then you don't have to worry about
when different references in your program are aliases of each other or not.
This is a huge burden lifted from you when you're trying to write correct,
easy to read, easy to maintain programs.
Then in section four, when we're discussing the equivalents of two
functions or two programs, we saw that if you can assume that both are functional,
both on have side effects, then a lot more things become equivalent.
In section five, we looked at the need to make copies of data if you're
worried that some other part of the program might update that data and
you don't want it updated.
But if you know that the program is not going to update something,
perhaps because the language forbids it, then you have to worry about that.
And so your programs connection should be more efficient, shorter and simpler.
And here near the end of the course when we studied subtyping, I made a big point
that in a mutable setting, depth subtyping is unsound.
It's incorrect.
It's wrong.
But if your data is immutable, then depth subtyping is in fact, not a problem.
Now I want to emphasize all these reasons and more to avoid mutation, but
I also want to emphasize that I understand there are situations where updating in
imperative state does make sense.
When it's the natural thing that you are doing when your program is modeling and
imperative update of the world, then imperative updates can make sense.
It's just not necessary to use them for every little algorithm in your program.
So, that's the last time I'll emphasize a mutation free programming.
Let me hit briefly some other highlights.
Function closures and how powerful, and convenient they are.
We didn't just teach what they are,
we saw idiom after idiom of how they are a powerful programming construct.
Back in part A of the course, I really emphasized datatypes and
pattern matching as a really powerful way
to organize the cases of your program that a lot of languages don't have.
And so, we saw that come back in the last programming assignment of the course where
you had some pretty concise ML code that you then had to port to
ruby in a way that was probably a bit harder to understand.