I am teaching a beginners course on JavaScript programming and web development. Passing a function as a value into another function is a very common thing in JS from the very beginning.
As the students are total beginners untouched by programming concepts, I need to be very careful and thorough when explaining abstract things such as treating functions as values. Thus I am searching for a good real-world analogy for higher order functions. The best ideas I had so far feel very artificial to me. For example
A hot dog stand is like a function. You put you order in as input and you get your hot dog out as output. If the hot dog stand would be a higher order function, you would come with your own toaster and hand it to the serving person saying: “Hey, use this toaster to toast the bun!”. The toaster is also like a function. So you are handing a function (toaster) as an input to another function (hot dog stand).
Let’s by honest. When did you last went for a hot dog with your own toaster in hand? Does anyone have a better analogy which would feel more real and be illustrative at the same time?
10
Have you considered if analogies are even useful for teaching this concept? In my experience analogies are typically more confusing than helpful. Often they end up more complicated than the thing they should explain.
If your students understand functions and they understand parameters, functions as parameters follow naturally. You just need to provide some simple examples and exercises to make them familiar with the concept.
You can use Array.map()
as an example. Given an array of numbers, how do they write a function which doubles every number? A few exercise like that, and they should feel comfortable with the concept.
2
The bar analogy
You go to a bar, and order a drink. That’s a function call. You get a concrete drink as result back.
The higher order-function is like the call to friend: you go to the bar and order a drink, but tell you want a special recipe and give a phone number to the barman/maid to call to get that special part of the recipe. That’s a higher-order function.
You can also go to the bar, order a drink, but give yourself the instructions for the special recipe. That’s making use if a lambda function/ closure.
The subcontractor analogy
You produce cars. But you cannot do everything by yourself. So you subcontract the engine from a supplier S
. The call to that supplier is a function call S()
. The result is an engine.
Now you may want that the pistons are of a special quality. So you agree on terms and conditions to get those pistions that correspond to certain specifications to a specialist P. You could call the specialist and get the pistons pistons=P()
and call the subcontractor and provide the pistons yourself S(pistons)
. but this is tedious and you stay involved everytime pistons are needed.
This is where you come to the idea to call the subcontractor S to deliver you some engine but to to get pistons directly from P. That’s a higher order function: S(f)
where S
gets the responsibility to call f()
whomever it is.
2
While I think Christophe’s analogy about calling the bar to order a drink is good, this analogy includes the dynamic nature of program data.
And I decided to have a little fun as well.
The Bus Driver With No Watch
Their once was a bus driver who had no watch. The bus route was precise. The driver was not. Each day they were late, or early, or worse. The riders were angry, upset and would curse.
“Why are you never on time?” they would quander. “It’s as if you are lost, and constantly wander.”
The driver replied, “No one is quicker on gas pedal or brake, but when it comes to telling time I quiver and quake. I have an old cell phone whose screen has been cracked. I can make and take calls, but to see the current time, I am jacked!”
“No problem,” said a rider, who gets off at the next stop. “You can dial a phone number like an audible clock. You can use your broken phone and dial 1-2 then 3, and a robot recording gives date and time all for free.”
At each stop the driver dialed 1-2 then 3, and the robotic recording recited the current time with glee. The phone number was like a Higher Order Function, you see. Though you can pass it around, copy it, or give it a name, anyone can call it, and the logic is same.
1
You tell an assassin to find a man with this name, and when he does, shoot him with this gun. You could invent these analogies all day.
However if I were explaining it to somebody who understood the basics of calling methods and subroutines, I would simply explain the reality of the matter, which is that one calls a method, and in doing so passes in a reference to a second method, the latter of which is later called from somewhere inside the former.
The exact method which is later called (the one called second from inside the first), rather than being stated explicitly at the call site (and so effectively being hard coded), can instead be varied according to what is passed as a parameter to the first method. A purely verbal explanation may be unclear, but it would not be difficult to demonstrate this on paper when using a worked example with some basic code in the language of choice.
If I remember correctly, the most complicated thing about “higher order functions” when I first encountered them was working out why they had a grand name as “higher order functions”. In fact they are just ordinary methods.
Potentially, what is new to the learner is the concept of a method being referenced (and that reference being assigned to variable) without the method being called – in other words, the concept of a method pointer or function pointer, and variables which can hold such references and be typed as such.
A “higher order function” then – at least in mainstream languages – is any method which includes a method pointer as a parameter. Why do such methods get a special name, where methods with various other types of parameters do not? This has more to do with mathematical theory, than it does for the practical reality of computing or programming.
In my view, it is method pointers (as a kind of variable or type) which are more likely to be considered a special kind of thing by programmers. Much more so than a method with such a parameter is likely to be considered a special kind of method, when for all intents and purposes the mechanics of the call and the passing of the arguments are identical whether the method be “higher order” or not.