Alright. In this segment,
I want to show you how to define functions inside of other functions.
When it's good style to do that and how to do it in a way that's good style.
And the neat thing about this segment,
what I find really exciting is I don't have to
teach you any new language constructs to do this.
All I have to do is point out to you that
the let-expressions we've already learned about have everything that we need.
And the key point is that the bindings in a let-expression
are any bindings in the language and we've
already learned that functions are just bindings.
So anywhere we have a let-expression we could have some of
those bindings b functions that then can only be used inside that let expression.
There's a very natural idea and it's actually pretty
surprising that more languages don't have something like this.
So why don't I show you an example and why don't I start this example without using let.
Just do this the old fashioned way of having
a helper function not defined inside of the function that it's helping.
So suppose that I define a little helper function count,
all this is going to do is take two integers from int to and
return a list of the integers between them.
So I'm going to do this inclusive,
so from equals to.
How about the one element list holding to or equivalently holding from.
Otherwise let's start with from and cons that on
to the recursive call from plus one and to.
So if you call this with arguments like three and six,
you would end up with the list 3 4 5 6 something like that.
Okay? But that's not the function I actually wanted.
That was just a helper function.
What I actually wanted was a function that counts up from one.
So it just takes a single argument x of type int and while this is really
easy now with our helper function just counts from one to x.
So let's make sure that works,
that I got that right.
Okay looks like I've got two functions there count and
count up from one and I got the right answer.
All right. Now this is fine.
This is good functional programming.
Unless this count function should really be private to count up from one.
I don't want the rest of the world to be bothered with
it or to know how it's implemented or anything like that.
Then I can use a let-expression for that.
So I could just say let, in,
end and then literally pick this up and
say I only want this count function to be in scope,
to be in the environment over here.
So now count can be used recursively because that's how function bindings work.
It could be used for any later bindings in this let expression.
I don't happen to have any.
And it can be used in the body.
So this will also work just fine.
And if I go over here to my REPL and restart and use the file again,
you'll see that there is no count function at the top level.
If I try to count it's just not there but it's still
a perfectly fine helper function being used to implement count up from one.
Okay. So that seems good.
That seems like good style if we wanted count to be private.
But now let me show you something even better,
even more fascinating and quite natural once you get used to it.
And that is to recognize that here where I defined count there's no reason to have a to.
Let's look at how this to variable is used
when we call count up from one with some value for x.
Suppose we call it with seven like we just tried out.
Well then x is going to be seven.
So we're going to call count where to is seven.
We'll say that one is not equal to seven
and so on then we'll do a recursive call where we
increment from but to is seven on the recursive call. Just like it was before.
In fact every time we call count recursively to is going to be seven.
And yet why should we have to pass that around when we already
have a variable that's a perfectly good value that we need, namely x?
What if I used x everywhere I was using to?
That makes perfect sense because here in
this let-expression and therefore in this function binding,
x is in my environment it's a parameter out here so I can use it.
Even this helper function can use x so this code will work just fine.
But now we have some really strange style
because I have this argument to that I'm never using.
So let's get rid of it. Let's get rid of it here,
let's get rid of it in our call and then sometimes people forget to do this,
you better get rid of it in the recursive call too.
So you don't have to write it in terms of to and then get rid of it once you get used to
this style of programming and recognizing that you can use variables in the environment.
We could have just written this version of count like this to begin with.
Okay. Now let's make sure we got that right.
That's the final version I wanted to show you.
I've restarted reinclude my file.
And let's try it out on seven, still works great.
Notice count is still of course not in
the environment outside of the function that defined it.
So that's the code I wanted to show you.
Now let's just go back to the slides and emphasize a couple of things about this.
This is the version I showed you where we do have a local function,
a nested function, definition count is inside of count from one.
But I'm still using this unnecessary to parameter.
Okay? So that's a good starting point to understand why this works and
understand that this is good style if
count is only going to be used by count up from one.
But if you want count to be usable in larger parts of
your program then you should put it somewhere where other code can use it.
Then the second version I showed you was this fancy version,
if you will, where count doesn't have a to parameter.
For this to work it's going to have to be defined in some scope where you
have the variable x so that you can use that in place of to.
And I just can't emphasize enough that this is using all the rules we've already learned.
We're just going to look x up in the environment where we defined
this function count and it's there and it has the value we need, so we just use it.
So when you can do this you really should.
It's certainly poor style to pass extra parameters that you don't need.
Why have a to argument when x will work just fine?
All right? So taking a step back
when should you use nested functions and when shouldn't you?
Well nested functions are great style if you want to make sure
that the function you're defining is only going to be used there in that let-expression.
If it's not going to be useful elsewhere it just keeps your code cleaner to do that.
If you're worried that other code might misuse it in
some way like you need to call the function in a certain way
for it to work correctly then limiting
the scope of where it can be used makes it easier to check that.
And in fact it makes it much easier to deal with
any changes you might make to that function in the future.
When you're maintaining your code you change a function you need to check all the uses of
that function and if you restrict where
the function can be used that process can be a lot easier.
Now that said nested functions are not always the right thing.
And so this is a real fundamental tradeoff when you're developing software.
You want to put your functions in
a narrow enough scope that you can restrict how they're used
but a wide enough scope that they can be reused as much as is helpful in your program.
But in any case we've now seen in ML now how to define
nested functions and how to do them in a way that's quite convenient and good style.