0:00

In this session, we are going to cover an important concept in functional

programming. Higher order functions let you pass

functions as arguments and return them as results.

One thing particular about functional languages is that they treat functions as

first class values. This means that.

Like any other value a function can be passed as a parameter to another function

or returned as a result. We'll see in this session that this

provides a flexible way to compose programs.

Functions that take other functions as parameters, or that return functions as

results are called higher order functions. So, that's the opposite of first order

functions. A first order function would be, then, a

function that acts on data types, simple data types such as [inaudible] or longs or

lists, but not other functions. Whereas, a higher order function acts on

other functions. Let's see an example.

Let's suppose we want to take the sum of all the integers between a and b.

So we could write something like this. Sum iints, it takes the, the bounds as

parameters, the and the b. And we ask well, if a is greater than b,

then there is nothing to sum. And we would return zero as the, logical

value for that. And otherwise we would return a + a

recursive call of sum ints of a+1 and b. Good.

Now that we've done that, let's vary the problem a little bit and consider taking

the sum of the cubes of all integers between a and b.

So here's the cube function. Take an x, return x times x times x.

Some cubes then would be the same as some ints.

But, where we used an A before now we return cube of A.

And otherwise, it's the same thing as some ints.

Recursive call to sum cubes in this case. Next, let's take the sum of all factorials

of the integers between a and b. Well, you see the principle by now.

The program, again, is exactly like sum ints and sum cubes, except that where

previously we computed the q, we compute the factorial.

Of course, these are all special cases of the mathematical sum of the values of f of

n, where f is the given function, and n is taken from the interval between a and b.

The questions is, well if mathematics has a special notation for that, shouldn't

programming have one as well. Can we factor out the common pattern into

a single method. So here's how that's done.

Let's define a function sum which takes a parameter F of type INT to INT, and to two

bounds A and B, both INTs. And let's generalize the previous, three

definitions as follows. We write, if A greater than B, then zero,

otherwise, take F of A plus sum of F and A plus one.

B. So the new thing here is that F is a

parameter of the sum function. It's not a given function.

It's a parameter. Once we have that, we can then write our

function sum in sum cube, sum factorial as follows.

Sum. Ints would be simply sum of id of ab,

where the id function simply returns its parameter unchanged.

Sum cubes would be sum of cube and AB, where the cube function is as what we've

seen before. And finally, sum factorial is sum of fact

and AB, where the fact function is, again, the factorial function.

So what we've done, effectively, is reuse the pattern that defines the sum function,

so that we had to write that only once. And we could reuse it in the three

definitions of the particular sums. One thing that's new here is a function

type. Function type is written A aero B, where A

and B are types. And its a type of a function that takes

some argument of type A, and returns a result of type B.

So for example Int aero Int, is a type of functions that map integers to integers.

4:24

Looking at the previous example again, we have successfully shortened the definition

of some ins, some cube, some factorials. But there's an annoying detail.

You have to name all the little auxiliary functions that we do here.

So, we've gained some eh, programme called here but we've spent more in the

definition of all these auxiliary functions.

So we've seen that passing functions as parameters can lead to the creation of

many small functions, and sometimes that's tedious.

Compare the situation to other types such as strings.

We could write something like Def STR equals ABC and then print an STR, But we

don't have to. We can just as well write print on ABC

directly and that's because strings exist as literals.

Since functions are important in our language, it makes sense to think of

introducing literals for functions as well, and that's what we do next.

These literals are called anonymous functions, because they do not have a

name. So here is how we write anonymous

functions. There is an example let's consider the

function that raises this argument to a cube that would be written as you see

here, sorry, takes a parameter X of type int, and then there comes an arrow and then

comes the value of the function In this case, case X times X times X.

So X colon INT is the parameter of the function, and on the right hand side of

the arrow is the body. In fact, the type of the parameter can be

omitted if the compiler can infer it from the context.

And if you have several parameters, you can write them in a list separated by

commas. So, for instance, that function here.

What would that be? Well yes the summation functions so it

would take two integers X and Y, and give you the sum of X and Y.

You could ask, are anonymous function essential for Scalar?

Just as, let's say, conditionals or value definitions are essential.

Or can they be defined in some other way? It actually, it turns out that every

anonymous function can be expressed in some other way, using a def.

So let's say you have an anonymous function with n parameters x1 to xn and a

body e. You can always write this as the function

def f. Then come the parameters, then comes the

body, and then you follow that by a reference to this function that you have

just defined. Sometimes you might have to write the

expression in a block, so that it, it doesn't, the name doesn't get confused

with something else. Because we can always rewrite anonymous

functions, we can therefore say that they are syntactic sugar.

They make writing functions sometimes easier, but they're not essential in the

sense that they do not add anything to the fundamentally expressive power of the

language. So to come back to the sum INTs and sum

cubes example, using anonymous functions, we can write these now in a shorter way.

Sum INTs would be just the function sum, where the function we pass is the identity

function, written X arrow X, and to balance our, the parameters, and sum cubes

would have as the function parameter X arrow X times X times X.

And if we do that, then we do not need anymore the auxiliary definitions of

identity or cube. Let's do an exercise.

The sum function uses linear recursion, can you write a tail recursive function

instead? Note that, unlike factorial, some really

could profit from this tail recursion optimization.

Because of the interval between A and B is large, you might get a lot of different

recursive steps so you might risk a stack overflow.

So let's rectify that, and design a tail recursive function instead.

I've already given you a template for this function.

8:31

It uses a nested function loop. And all you have to do is replace the

triple question marks here and here. So let's see how we would go about that.

The first one here is we, in the loop function we have a test that says well if

something is true then probably that would be the termination.

So when do we terminate. Well we terminate if the lower bound A is

greater then the upper bound B. And what do we return in that case?

Well, since we have used the accumulator trick, we would return the accumulator.

Once we have that, we can look at the initial call, so as the first parameter,

we would pass the initial lower bound A. And as the initial accumulator would have

to be what. Well, we know accumulator is the result

that we return, and the interval is initially empty,

so that would have to be zero. And finally, in the recursive call, we

would assume that the initial bound gets incremented by one.

And the new accumulator is F applied to A plus the previous accumulator.

You can test this function. Let's apply this function to lower bound

three, upper bound five. And you would get 50, as expected.

You have seen higher order functions as essential building blocks of functional

programs. In the next session we are going to play a

little bit more with higher order functions and introduce new ways to express them.