05 July 2023
Confused about: Subroutine, Procedure, Function, and Method?
Hands up if you are quite clear on the difference between these four terms: Subroutine, Procedure, Function, and Method …
No, I thought not. Don’t worry, you have every right to be confused! Part of the problem lies simply with computing history: these terms appeared at different points; each has evolved over time; and each is used differently in different programming communities.
Personally, I love computing history, but I think I would very quickly bore you with too much detail. Instead I am going to tackle what's in front of us right now, and in the specific context of teaching GCSE or A-level Computer Science.
If you are struggling with these terms I hope to bring some clarity, and even if you are confident with them, I will offer and insight that might still be very new to you. If you can embrace this you will gain a deeper understanding of good programming patterns, and if you can teach it, you will be doing a greater service to your pupils.
Subroutine
Both the OCR and AQA GCSE specifications use the word 'subroutine' simply as a convenience to refer to a chunk of code that is either a 'procedure' or a 'function'. In other words a subroutine, in our present context, is not a specific coding pattern in its own right (historically this was different). We'll come to the difference between a 'procedure' and 'function' – which is very important – shortly.
Subroutines are 'free standing' – you 'call' (or 'invoke') them in code directly by their name (sometimes qualified by a prefix such as Math.Sin), and pass in one or more arguments corresponding to the 'parameters' (if any) defined by the subroutine.
Method
Leaving aside the more general use of the word 'method' to refer to a process or technique employed by humans when designing a system, in strict context of program code, the term 'method' is used only at A-level in the context of object-oriented programming (OOP).
In OOP a method is a subroutine (still just a word that can refer to either a 'procedure' or a 'function') that is defined wihtin a class – rather than being freestanding. Most commonly (and in the context of the OCR A-level spec, always) the methods you will encounter are 'instance methods'. This means that the method is called on an instance of that class. Thus if you have a class named Player, and an instance of Player held in a variable named p1, you can invoke the method calculateScore, using 'dot syntax' like this: p1.calculateScore().
(AQA A-level requires the pupil to know about 'static' methods. These are defined on a class, but work like freestanding subroutines – they don't apply to an instance of the class. The word 'static' is actually specific to the C family of languages; in VB they are known as 'shared' methods; in Python there is no distinction between a 'static' method and a freestanding function or procedure.)
Unfortunately, in the broader world, while some programming communities use the term 'method' in exactly the way described above, in other communities 'method' is used more broadly: to refer to any function or procedure whether defined on a class, or freestanding. You may come across this broader usage in messages from your programming language compiler or interpreter.
The difference between a function and procedure
In the context of GCSE or A-level programming, the only difference between a procedure and a function is that the latter 'returns' a value, and the former does not.
In Python, you cannot tell the difference between a function and a procedure unless you look inside the code, to see if there are any 'return' statements and, if so, whether they are followed by a value or not.
In VB, the syntax for defining a function is distinct from that of a procedure, starting with two different keywords, Function and Sub respectively (confusing in the current context – that's history leaving its mark again). If you have your environment set up correctly, then it should enforce the rule that a function must return a value (of the correct type defined in the function's signature) and a subroutine may not.
In C# the syntax for defining a function and a procedure is essentially the same except that the 'return type' for a procedure uses the keyword void. (Despite my being a fan of C# as a professional developer, I will admit that this syntax is horribly unfriendly in the classroom).
But why is there any difference?
But, the important question – and you might have had this from brighter pupils - is not 'What is the difference?' but 'Why is there any difference?'. In other words, why use two different names when the only difference is whether or not you return a value?
This is a hugely important question, and it is glossed over in the specifications. Because, between the boards and the program language designers – they've missed the main point!
A proper function is not a subroutine that happens to return a value. It returns a result that depends deterministically, on the values passed to it via its parameters, and on nothing else. And it has no side effects. In other words, pass the same values into a proper function and it always returns the same result, and leaves no trace of the fact that it has been called!
This means that a proper function cannot contain any input, print, or other I/O calls; cannot alter the values passed into it as parameters, and cannot have any other effect that is observable outside the function. A proper function in programming works just like a mathematical function, such as sine/cosine, raising to a power, integration or differentiation.
A procedure has none of these constraints – you can do more or less anything you like in a procedure. It therefore follows that within the body of a procedure you can call other procedures or functions, but in the body of a function you may only call other functions, never any procedure.
The problem is that the GCSE/A-level boards have not embraced this important distinction – and who can blame them when none of the languages used in British schools enforces it? Given this status quo it would be better if the boards just used the word 'procedure' throughout – and accept that a procedure might or might not return a value or not, like Python does.
And given that procedures appear to be far more flexible than functions anyway, surely this would be a good thing? No. Once you understand the distinction properly you eventually realise that a function is a far more powerful and important idea than a procedure – and that good programming means, amongst other things, putting as much of the logic of your system into functions and as little as possible into procedures. You still need procedures to handle the interface with the 'real world – whether in the form of a user interface, storage, networking, or other physical devices such as robotic sensors and motors.
When you don't have a proper distinction between functions and procedures, what you end up with is everything in procedures even if some of them get called 'functions'!
The principle of 'Command Query Separation' (CQS)
Still confused? Let me explain this another way – using the words of the great computer scientist Bertrand Meyer. In the early 1980s, while designing the programming Eiffel (the name gives a clue as to his nationality) Meyer several key design principles for good programming, one of which is known as 'Command Query Separation' or CQS.
In the terms we have been using above, CQS means that every subroutine (Meyer used the term 'method') should either be a 'command' or a 'query'. A command tells the system to make some change. The system is different after the command has been given. A query just asks for information either to be extracted from the system, or constructed new from existing information. The reason they need to be kept separate, Meyer said, was because 'asking a question should not change the answer' (this is a simplification, but it is a very well made point).
Now when the distinction is made between proper functions and procedures, we can see that all proper functions are queries, but we need procedures when we need to make changes. (As our leader SPJ has said, 'functional programming means programming with functions that have no side effects – but the whole point of systems is to have side effects!'.)
So we should use procedures precisely to create the side effects that we want: writing to a screen, or disk, sending an email across the network, moving the robot forwards. But we should be using proper functions for all the transformations of information required in any system. And by putting those transformations into proper functions we never get any unwanted side effects, which are the most common causes of bugs.
Because with proper functions, if you can prove that if both functions A and B are correct in their implementation, then they may be combined in any way (such as calling A passing in the results of calling B, or vice versa) and the combination is guaranteed to be correct. The same is simply not true of combining procedures A and B – because while each might work perfectly in isolation, they can fail (or produce the wrong result) when combined, because their respective side effects interfere with each other.
So what can you do?
Sadly, until we get a programming language that enforces the proper distinction between a function and a procedure in schools (languages such as Haskell do enforce this, but they are not very friendly for school level teaching) making the distinction depends on self-discipline. But it really isn't very hard. Just follow these rules when writing a function:
- Don't print, input or make any calls to the system in a function. Do that only in procedures.
- Don't let your function depend on any variables defined outside the function. (Global constants – if your language supports them - are OK as they don't change once the program is compiled)
- Don't, in a function, change any value that is passed in as a parameter. That means: don't re-assign any of the parameters to a new value, but also don't make any changes to any types that may be 'mutated' (lists, arrays, instances of your classes). In other words, treat the parameters as strictly 'input only'. In a procedure, it is OK to make such modifications such that changes made inside the procedure may be observed in code outside the procedure after the procedure has exited.
- Don't call any procedure from within a function body; the reverse is OK.
- Do push as much logic as you can from procedures (including 'main' which is just the top-level procedure) into functions. That makes your code safer, and more reusable.
Follow these rules and your code will be easier to read, easier to modify, and less prone to those hard-to-track-down kind of bugs.
Discussion
Please login to post a comment
@rmillwood Introducing them as early as possible worked well with A level students, even getting the old chestnut of “Hello world” to be called via a function, returning a string then passing in a message etc… So, lesson 1!
Once upon a time OCR used the BCS Glossary to inform subject content. It is in there but I can’t say I used the term until I taught the OCR spec.
I missed that, sorry. In the A-level spec OCR refers to functions and procedures as ‘subroutines’.
The terminology is not only confusing, but inconsistent within one board!
(I even found one reference to ‘sub-procedure’.)
Unfortunately OCR uses this language in the GCSE specification
“The use of sub programs (functions and procedures) to produce structured code”
I don’t think ‘sub-program’ is used in any of the school specifications. The four that I chose all are.
Would it be wrong of me to mention ‘sub-program’ as another term?
Also, another questionable generalisation (on my part): functions are called as part of expressions, or wherever a value is sought, whereas the rest are used as ‘commands’?
Where in our various curricula do we explicitly mention all these? When should they be introduced?
I feel they are as important as sequence, selection and iteration as key algorithmic concepts.
Papert went as far as to say: ""The idea of programming is introduced through the metaphor of teaching the Turtle a new word.”
(Mindstorms, 1980, p12)
So he would begin with them!
This was really helpful.
As you say the ‘Why is there a difference’ question has caused my difficulties in the past.
Much appreciated.