Function is an important concept in programming, mathematics, and many other fields. However, throughout my 3 years of learning about functions in mathematics in my high school up till my foundation year in university, I still don’t quite get the concept of what functions really are, or are meant to represent. Yes, the formal definition, along with the visual representation of functions do stuck with me, but again, I’m paralyzed when it comes to explaining it to another person who’s completely foreign to the concept.

It wasn’t until when I wrote my first program in C language back in 2016, did the lightbulb lit up all of a sudden. It was like a slap to my face that really opened my eyes to what I’d been staring right up front at for so many years.

I hope this post would explain this much mislead concept in a much simpler manner to you, and hopefully, your bulb would light up as well.

P.S. This explanation is sorta tailored specifically towards fellow beginner programmers, hence the some of the terminologies might not stick to those who aren’t in the field. But still, if you don’t mind, read on!

# LEVEL 1 – CORE CONCEPT

Imagine a weighing machine.

This is a simple weighing machine, that does one and only one thing – weighs out 500g of any given solid item – flour, sugar, sand, bananas, cats etc.

(illustration of weighing machine)

Aaaand there you have it! There it is, an everyday example of a function.

A function, can be visualized as a machine could carry out some sort of operation.

# LEVEL 2 – DEEPER DIVE

Taking a closer look at the weighing machine, it can be broken down into 3 parts – the input (the items to be weighted), the operation (the actual process of weighing), the output (500g of the given item). Using this analogy, we could understand a function the same way, just with different terminologies.

A function can also be broken down into 3 parts – the parameter, operation, and output.

Imagine, another mixing machine that mixes up ingredients. What if you wanted to mix up 500g each of flour, sugar, cocoa, and self-raising flour? Well, you could dictate implicitly in the mixing machine to weigh up 500g each of each ingredient and then mixing them, OR, you could utilize the weighing machine you had previously. All you had to do was to use one weighing machine to weigh out 4 different inputs, and channeling its outputs to the mixing machine to be mixed.

What you’ve just done there, is creating a composite function – a function made up of functions. And in this case, the mixing machine is the parent function of the weighing machine. The process of the mixing machine utilizing the weighing machine is known as calling. Once the weighing machine is called, it returns the output to its parent function.

You might’ve noticed that there is a slight difference between an output and a return. Both are the end products of a function, but the key lies in what happens to the them. An output of a function is to be extracted directly and used, while a return of a function is typically not extracted, but passed back to its parent function for future use. This can be compared with the difference between shooting and passing in the game of basketball. Both of them involve the player throwing the ball – but one is to gain a point immediately, while the other is to another player to achieve other goals (e.g. distracting the opponent, delaying the game, gaining a more strategically advantageous ground etc).

An output of a function is to be extracted directly and used, while a return of a function is typically not extracted, but passed back to its parent function for future use.

Imagine a scenario where you wanted to mix in different weights of different ingredients? Say, instead of 500g for each ingredient, you want 500g of flour, 100g of sugar, 200g of cocoa, and 500g of self-raising flour? With just some minor tweaks to your previous weighing machine and enabling it to take in both weight and item as its parameters, you could achieve it with one function! Now, with your new weighing machine, you could dictate how much of an ingredient do you want to weigh.

On top of that, this weighing machine could be used in many different larger parent machines, i.e. weighing out ingredients for a baking machine, or weighing out raw chemicals for a medicine producing machine. You could reuse the machine in different situations due to its generality.

Imagine another scenario. Instead of mixing dry ingredients, you now need to mix measured wet ingredients such as milk, water, melted butter etc. What do you do? You could create a measuring machine that could measure out liquids, and funnel the output to the input of the mixing machine instead! Simple!

With both of the scenarios above, I’ve illustrated two of the most powerful attributes of a function – versatility and the ability to composite.

With versatility, you could have one function with the seemingly ability to mimic several functions. With the ability to adapt to many situations, you would only have to build a function once, and be able to use it in multiple other situations.

With the ability to composite, you would be able to build huge functions using multiple smaller functions. This not only promotes readability (because it is much intuitive to read functions compared to reading lines after lines of explicit code), but also ease the process of modifying and maintaining code (if one part of it is faulty, you could easily extract the faulty function out, instead of having to crop out the lines of code that performs the function manually.

# LEVEL 3 –  NITTY GRITTY BITS

Functions are conventionally written as

function_name (parameters), whereby italics are placeholders for the item described.

So something like cook(meal), would be described as a function cook() that takes in meal as its parameter.

When meal = dinner would mean cooking dinner, and when meal = lunch would mean cooking lunch.

Looking back at our weighing and measuring machines, they could be described as

weighing(weight, item)

measuring(volume, item)

# ENLIGHTENMENT

As you build bigger and bigger programs, sometimes it’s unpractical to understand the program line by line because of its sheer quantity. Rather, you would have to shift your perspective to higher ground, and look at things from the process of functions calling other functions. At this perspective, the individual lines of codes aren’t visible – but rather you’ll be seeing this orchestra of parent functions calling several daughter functions to complete tedious, long operations in what seems like a blink of an eye.

– Just another dude