The Principles of Software Engineering #3: Function
From the point of view of building a compiler this isn't exactly rocket-science, but just to recap on the fundamentals a Function is a named block of code which:
- may be executed from some select area, maybe everywhere, in the rest of the code, and always returns control to the caller, possibly passing back a return value,
- takes parameters, which may be positional or named, by value or reference, mandatory, optional or even of undetermined number
- has its own scope and can declare local variables
Functions are the way (and for a long time were the only way) to manage complexity via Functional Decomposition (FD), the process by which blocks of code are split into functions in order to make the code easier to understand (and therefore easier to get working). FD aids comprehensibility by reducing duplication (i.e. removing clutter) and adding english language explanation in the function and parameter names. FD helps if, and only if, it is done well. Done badly it makes things worse.
Functions also introduce a concept which is so fundamental and mind-blowingly important to programming that it is all too easy to miss. Every function has a user and a provider: a separation of concerns which is the beginnings of Client-Server programming. User and provider need to communicate effectively since neither should know what is going on in the other's domain (i.e. for an effective separation and simplification of complexity the function writer should know nothing about how and where his function will be used and the function user should equally know nothing about how the function has been implemented). Programming languages give very little help in easing this communication. Documentation is the frequent resort of the inexperienced programmer but the best result comes from good FD (i.e. design) and the best possible choice in function and parameter names.
Steve Maguire in his book "Writing Solid Code" was the first person to take a close look at FD from the point of view of reducing bug-risk. I had been programming in C for 9 years when I came across this and I found it a real eye-opener. In particular until then the attitude of the function writer to the function user was one of "caveat emptor" (or "user-or"), even if the user and writer were one and the same person. Steve Maguire suggested that the writer should try to help the user not make mistakes when using his code by not writing functions which tripped the user up. Where Functions had divided the programming world with a big fence (frequently called the function library), this book tried to build a bridge and persuade the two groups of programmers to be nice to each other!
The section on Assertions is the only part of the book that I'm not sure I agree with, as my friend Sean once pointed out to me you shouldn't change the behaviour of a function between debug and release versions. I accept that, but the rest of the book is faultless and excellent.
Ask any group of programmers what language they program in and you'll get the expected answer (C, C++, C#, Java, etc). The truth is that we all program in english (or whatever our spoken language is). We punctuate in a computer language, but 90% of the information we gain when we read through a program comes to us in our native language. Although I shall come back to this later, I strongly believe that our native language skills are fundamentally important in the way we program, and that we need to think more about how we use our vocabulary and such things as prepositions, adjectives and so on. For now let me just say that trying to understand code when you have to refer to documentation every time you come across a function is like trying to read a book with a dictionary at your side - cumbersome and slow.
Functions are generally named according to "intent". One debugging technique I have found very useful is renaming functions from "intent" to names which reflect what the functions actually do, even if these names are somewhat long (e.g. rename "register_customer" to "register_customer_name_if_not_present_or_out_of_date"). The latter might seem cumbersome, but follow the trail of functions up and you may spot where user and writer have made different assumptions about what the function is doing. I wish we could also do this with parameter names but unfortunately there has been a decline in the provision of keyword, or named, parameters in programming languages (e.g. compare "register_customer(true,false)" with "register_customer(account_holder=true,overseas=false)", though I note that C# is thinking about bringing this back on a future release).
Before the days of OO all you could do by way of code design was FD plus gathering functions together into modules and libraries. This latter activity was "nice" but not really what design was all about - that was the FD. Now that we have, wisely, moved away from the design-up-front approaches of the past, FD has become even more important as an ongoing refactoring activity, as functions coalesce and explode in our drive to produce a solution which is rational and comprehensible.
Richard


No feedback yet
Leave a comment