Callable units provide a powerful programming tool.[2] The primary purpose is to allow for the decomposition of a large and/or complicated problem into chunks that have relatively lowcognitive load and to assign the chunks meaningful names (unless they are anonymous). Judicious application can reduce the cost of developing and maintaining software, while increasing its quality and reliability.[3]
Callable units are present at multiple levels ofabstraction in the programming environment. For example, aprogrammer may write a function insource code that is compiled tomachine code that implements similarsemantics. There is a callable unit in the source code and an associated one in the machine code, but they are different kinds of callable units – with different implications and features.
Someprogramming languages, such asCOBOL andBASIC, make a distinction between functions that return a value (typically called "functions") and those that do not (typically called "subprogram", "subroutine", or "procedure"); some, such asC,C++, andRust, only use the term "function" irrespective of whether they return a value or not; others, such asALGOL 60 andPL/I, only use the wordprocedure. Someobject-oriented languages, such asJava andC#, refer to functions insideclasses as "methods".
The idea of a callable unit was initially conceived byJohn Mauchly andKathleen Antonelli during their work onENIAC and recorded in a January 1947 Harvard symposium on "Preparation of Problems forEDVAC-type Machines."[4]Maurice Wilkes,David Wheeler, andStanley Gill are generally credited with the formal invention of this concept, which they termed aclosed sub-routine,[5][6] contrasted with anopen subroutine ormacro.[7] However,Alan Turing had discussed subroutines in a paper of 1945 on design proposals for the NPLACE, going so far as to invent the concept of areturn address stack.[8]
The idea of a subroutine was worked out after computing machines had already existed for some time. The arithmetic and conditional jump instructions were planned ahead of time and have changed relatively little, but the special instructions used for procedure calls have changed greatly over the years. The earliest computers, such as theManchester Baby, and some early microprocessors, such as theRCA 1802, did not have a single subroutine call instruction. Subroutines could be implemented, but they required programmers to use the call sequence—a series of instructions—at eachcall site.
Subroutines were implemented inKonrad Zuse'sZ4 in 1945.
In 1945,Alan M. Turing used the terms "bury" and "unbury" as a means of calling and returning from subroutines.[9][10]
In January 1947 John Mauchly presented general notes at 'A Symposium of Large Scale Digital Calculating Machinery'under the joint sponsorship of Harvard University and the Bureau of Ordnance, United States Navy. Here he discusses serial and parallel operation suggesting
...the structure of the machine need not be complicated one bit. It is possible, since all the logical characteristics essential to this procedure are available, to evolve a coding instruction for placing the subroutines in the memory at places known to the machine, and in such a way that they may easily be called into use.
In other words, one can designate subroutine A as division and subroutine B as complex multiplication and subroutine C as the evaluation of a standard error of a sequence of numbers, and so on through the list of subroutines needed for a particular problem. ... All these subroutines will then be stored in the machine, and all one needs to do is make a brief reference to them by number, as they are indicated in the coding.[4]
Kay McNulty had worked closely with John Mauchly on theENIAC team and developed an idea for subroutines for theENIAC computer she was programming during World War II.[11] She and the other ENIAC programmers used the subroutines to help calculate missile trajectories.[11]
Goldstine andvon Neumann wrote a paper dated 16 August 1948 discussing the use of subroutines.[12]
Some very early computers and microprocessors, such as theIBM 1620, theIntel 4004 andIntel 8008, and thePIC microcontrollers, have a single-instruction subroutine call that uses a dedicated hardware stack to store return addresses—such hardware supports only a few levels of subroutine nesting, but can support recursive subroutines. Machines before the mid-1960s—such as theUNIVAC I, thePDP-1, and theIBM 1130—typically use acalling convention which saved the instruction counter in the first memory location of the called subroutine. This allows arbitrarily deep levels of subroutine nesting but does not support recursive subroutines. TheIBM System/360 had a subroutine call instruction that placed the saved instruction counter value into a general-purpose register; this can be used to support arbitrarily deep subroutine nesting and recursive subroutines. The BurroughsB5000[13] (1961) is one of the first computers to store subroutine return data on a stack.
The DECPDP-6[14] (1964) is one of the first accumulator-based machines to have a subroutine call instruction that saved the return address in a stack addressed by an accumulator or index register. The laterPDP-10 (1966),PDP-11 (1970) andVAX-11 (1976) lines followed suit; this feature also supports both arbitrarily deep subroutine nesting and recursive subroutines.[15]
In the very early assemblers, subroutine support was limited. Subroutines were not explicitly separated from each other or from the main program, and indeed the source code of a subroutine could be interspersed with that of other subprograms. Some assemblers would offer predefinedmacros to generate the call and return sequences. By the 1960s, assemblers usually had much more sophisticated support for both inline and separately assembled subroutines that could be linked together.
One of the first programming languages to support user-written subroutines and functions wasFORTRAN II. The IBM FORTRAN II compiler was released in 1958.ALGOL 58 and other early programming languages also supported procedural programming.
Even with this cumbersome approach, subroutines proved very useful. They allowed the use of the same code in many different programs. Memory was a very scarce resource on early computers, and subroutines allowed significant savings in the size of programs.
Many early computers loaded the program instructions into memory from apunched paper tape. Each subroutine could then be provided by a separate piece of tape, loaded or spliced before or after the main program (or "mainline"[16]); and the same subroutine tape could then be used by many different programs. A similar approach was used in computers that loaded program instructions frompunched cards. The namesubroutine library originally meant a library, in the literal sense, which kept indexed collections of tapes or decks of cards for collective use.
On those computers, instead of modifying the function's return jump, the calling program would store the return address in a variable so that when the function completed, it would execute an indirect jump that would direct execution to the location given by the predefined variable.
Another advance was thejump to subroutine instruction, which combined the saving of the return address with the calling jump, thereby minimizingoverhead significantly.
In the IBMSystem/360, for example, the branch instructions BAL or BALR, designed for procedure calling, would save the return address in a processor register specified in the instruction, by convention register 14. To return, the subroutine had only to execute an indirect branch instruction (BR) through that register. If the subroutine needed that register for some other purpose (such as calling another subroutine), it would save the register's contents to a private memory location or a registerstack.
In systems such as theHP 2100, the JSB instruction would perform a similar task, except that the return address was stored in the memory location that was the target of the branch. Execution of the procedure would actually begin at the next memory location. In the HP 2100 assembly language, one would write, for example
...JSB MYSUB (Calls subroutine MYSUB.)BB ... (Will return here after MYSUB is done.)
to call a subroutine called MYSUB from the main program. The subroutine would be coded as
MYSUB NOP (Storage for MYSUB's return address.)AA ... (Start of MYSUB's body.)...JMP MYSUB,I (Returns to the calling program.)
The JSB instruction placed the address of the NEXT instruction (namely, BB) into the location specified as its operand (namely, MYSUB), and then branched to the NEXT location after that (namely, AA = MYSUB + 1). The subroutine could then return to the main program by executing the indirect jump JMP MYSUB, I which branched to the location stored at location MYSUB.
Compilers for Fortran and other languages could easily make use of these instructions when available. This approach supported multiple levels of calls; however, since the return address, parameters, and return values of a subroutine were assigned fixed memory locations, it did not allow for recursive calls.
Incidentally, a similar method was used byLotus 1-2-3, in the early 1980s, to discover the recalculation dependencies in a spreadsheet. Namely, a location was reserved in each cell to store thereturn address. Sincecircular references are not allowed for natural recalculation order, this allows a tree walk without reserving space for a stack in memory, which was very limited on small computers such as theIBM PC.
Most modern implementations of a function call use acall stack, a special case of thestack data structure, to implement function calls and returns. Each procedure call creates a new entry, called astack frame, at the top of the stack; when the procedure returns, its stack frame is deleted from the stack, and its space may be used for other procedure calls. Each stack frame contains theprivate data of the corresponding call, which typically includes the procedure's parameters and internal variables, and the return address.
The call sequence can be implemented by a sequence of ordinary instructions (an approach still used inreduced instruction set computing (RISC) andvery long instruction word (VLIW) architectures), but many traditional machines designed since the late 1960s have included special instructions for that purpose.
The call stack is usually implemented as a contiguous area of memory. It is an arbitrary design choice whether the bottom of the stack is the lowest or highest address within this area, so that the stack may grow forwards or backwards in memory; however, many architectures chose the latter.[citation needed]
Some designs, notably someForth implementations, used two separate stacks, one mainly for control information (like return addresses and loop counters) and the other for data. The former was, or worked like, a call stack and was only indirectly accessible to the programmer through other language constructs while the latter was more directly accessible.
When stack-based procedure calls were first introduced, an important motivation was to save precious memory.[citation needed] With this scheme, the compiler does not have to reserve separate space in memory for the private data (parameters, return address, and local variables) of each procedure. At any moment, the stack contains only the private data of the calls that are currentlyactive (namely, which have been called but haven't returned yet). Because of the ways in which programs were usually assembled from libraries, it was (and still is) not uncommon to find programs that include thousands of functions, of which only a handful are active at any given moment.[citation needed] For such programs, the call stack mechanism could save significant amounts of memory. Indeed, the call stack mechanism can be viewed as the earliest and simplest method forautomatic memory management.
However, another advantage of the call stack method is that it allowsrecursive function calls, since each nested call to the same procedure gets a separate instance of its private data.
In amulti-threaded environment, there is generally more than one stack.[17] An environment that fully supportscoroutines orlazy evaluation may use data structures other than stacks to store their activation records.
One disadvantage of the call stack mechanism is the increased cost of a procedure call and its matching return.[clarification needed] The extra cost includes incrementing and decrementing the stack pointer (and, in some architectures, checking forstack overflow), and accessing the local variables and parameters by frame-relative addresses, instead of absolute addresses. The cost may be realized in increased execution time, or increased processor complexity, or both.
This overhead is most obvious and objectionable inleaf procedures orleaf functions, which return without making any procedure calls themselves.[18][19][20] To reduce that overhead, many modern compilers try to delay the use of a call stack until it is really needed.[citation needed] For example, the call of a procedureP may store the return address and parameters of the called procedure in certain processor registers, and transfer control to the procedure's body by a simple jump. If the procedureP returns without making any other call, the call stack is not used at all. IfP needs to call another procedureQ, it will then use the call stack to save the contents of any registers (such as the return address) that will be needed afterQ returns.
In general, a callable unit is a list of instructions that, starting at the first instruction, executes sequentially except as directed via its internal logic. It can be invoked (called) many times during theexecution of a program. Execution continues at the next instruction after the call instruction when itreturns control.
The features ofimplementations of callable units evolved over time and varies by context.This section describes features of the various common implementations.
Some languages, such asPascal,Fortran,Ada and manydialects ofBASIC, use a different name for a callable unit that returns a value (function orsubprogram) vs. one that does not (subroutine orprocedure). Other languages, such asC,C++,C# andLisp, use only one name for a callable unit,function. The C-family languages use the keywordvoid to indicate no return value.
If declared to return a value, a call can be embedded in anexpression in order to consume the return value. For example, a square root callable unit might be called likey = sqrt(x).
A callable unit that does not return a value is called as a stand-alonestatement likeprint("hello"). This syntax can also be used for a callable unit that returns a value, but the return value will be ignored.
Some older languages require a keyword for calls that do not consume a return value, likeCALL print("hello").
Most implementations, especially in modern languages, supportparameters which the callable declares asformal parameters. A caller passesactual parameters, a.k.a.arguments, to match. Different programming languages provide different conventions for passing arguments.
Default in most Algol-like languages afterAlgol 60, such as Pascal, Delphi, Simula, CPL, PL/M, Modula, Oberon, Ada, and many others including C, C++ and Java
A reference to the argument is passed; typically its address
Selectable in most Algol-like languages afterAlgol 60, such as Algol 68, Pascal, Delphi, Simula, CPL, PL/M, Modula, Oberon, Ada, and many others including C++, Fortran, PL/I
Like a macro – replace the parameters with the unevaluated argument expressions, then evaluate the argument in the context of the caller every time that the callable uses the parameter
In some languages, such as BASIC, a callable has different syntax (i.e. keyword) for a callable that returns a value vs. one that does not. In other languages, the syntax is the same regardless.In some of these languages an extra keyword is used to declare no return value; for examplevoid in C, C++ and C#.In some languages, such as Python, the difference is whether the body contains a return statement with a value, and a particular callable may return with or without a value based on control flow.
In many contexts, a callable may haveside effect behavior such as modifying passed or global data, reading from or writing to aperipheral device, accessing afile, halting the program or the machine, or temporarily pausing program execution.
Side effects are considered undesireble byRobert C. Martin, who is known for promoting design principles. Martin argues that side effects can result intemporal coupling or order dependencies.[21]
In strictlyfunctional programming languages such asHaskell, a function can have noside effects, which means it cannot change the state of the program. Functions always return the same result for the same input. Such languages typically only support functions that return a value, since there is no value in a function that has neither return value nor side effect.
Most contexts supportlocal variables –memory owned by a callable to hold intermediate values. These variables are typically stored in the call'sactivation record on thecall stack along with other information such as thereturn address.
If supported by the language, a callable may call itself, causing its execution to suspend while anothernested execution of the same callable executes.Recursion is a useful means to simplify some complex algorithms and break down complex problems. Recursive languages provide a new copy of local variables on each call. If the programmer desires the recursive callable to use the same variables instead of using locals, they typically declare them in a shared context such static or global.
Languages going back toALGOL,PL/I andC and modern languages, almost invariably use a call stack, usually supported by the instruction sets to provide an activation record for each call. That way, a nested call can modify its local variables without affecting any of the suspended calls variables.
Some languages, e.g.,Ada,Pascal,PL/I,Python, support declaring and defining a function inside, e.g., a function body, such that the name of the inner is only visible within the body of the outer.
If a callable can be executed properly even when another execution of the same callable is already in progress, that callable is said to bereentrant. A reentrant callable is also useful inmulti-threaded situations since multiple threads can call the same callable without fear of interfering with each other. In theIBMCICStransaction processing system,quasi-reentrant was a slightly less restrictive, but similar, requirement for application programs that were shared by many threads.
Some languages supportoverloading – allow multiple callables with the same name in the same scope, but operating on different types of input. Consider the square root function applied to real number, complex number and matrix input. The algorithm for each type of input is different, and the return value may have a different type. By writing three separate callables with the same name. i.e.sqrt, the resulting code may be easier to write and to maintain since each one has a name that is relatively easy to understand and to remember instead of giving longer and more complicated names likesqrt_real,sqrt_complex,qrt_matrix.
Overloading is supported in many languages that supportstrong typing. Often the compiler selects the overload to call based on the type of the input arguments or it fails if the input arguments do not select an overload. Older and weakly-typed languages generally do not support overloading.
Here is an example of overloading inC++, two functionsArea that accept different types:
// returns the area of a rectangle defined by height and widthdoubleArea(doubleh,doublew){returnh*w;}// returns the area of a circle defined by radiusdoubleArea(doubler){returnr*r*3.14;}intmain(){doublerectangle_area=Area(3,4);doublecircle_area=Area(5);}
PL/I has theGENERIC attribute to define a generic name for a set of entry references called with different types of arguments. Example:
DECLARE gen_name GENERIC( name WHEN(FIXED BINARY), flame WHEN(FLOAT), pathname OTHERWISE);
Multiple argument definitions may be specified for each entry. A call to "gen_name" will result in a call to "name" when the argument is FIXED BINARY, "flame" when FLOAT", etc. If the argument matches none of the choices "pathname" will be called.
Aclosure is a callable plus values of some of its variables captured from the environment in which it was created. Closures were a notable feature of the Lisp programming language, introduced byJohn McCarthy. Depending on the implementation, closures can serve as a mechanism for side-effects.
Besides itshappy path behavior, a callable may need to inform the caller about anexceptional condition that occurred during its execution.
Most modern languages support exceptions which allows for exceptional control flow that pops the call stack until an exception handler is found to handle the condition.
Languages that do not support exceptions can use the return value to indicate success or failure of a call. Another approach is to use a well-known location like a global variable for success indication. A callable writes the value and the caller reads it after a call.
In theIBM System/360, where return code was expected from a subroutine, the return value was often designed to be a multiple of 4—so that it could be used as a directbranch table index into a branch table often located immediately after the call instruction to avoid extra conditional tests, further improving efficiency. In theSystem/360assembly language, one would write, for example:
BAL 14, SUBRTN01 go to a subroutine, storing return address in R14 B TABLE(15) use returned value in reg 15 to index the branch table,* branching to the appropriate branch instr.TABLE B OK return code =00 GOOD } B BAD return code =04 Invalid input } Branch table B ERROR return code =08 Unexpected condition }
Some optimizations for minimizing call overhead may seem straight forward, but cannot be used if the callable has side effects. For example, in the expression(f(x)-1)/(f(x)+1), the functionf cannot be called only once with its value used two times since the two calls may return different results. Moreover, in the few languages which define the order of evaluation of the division operator's operands, the value ofx must be fetched again before the second call, since the first call may have changed it. Determining whether a callable has a side effect is difficult – indeed,undecidable by virtue ofRice's theorem. So, while this optimization is safe in a purely functional programming language, a compiler for a language not limited to functional typically assumes the worst case, that every callable may have side effects.
Inlining eliminates calls for particular callables. The compiler replaces each call with the compiled code of the callable. Not only does this avoid the call overhead, but it also allows thecompiler tooptimize code of the caller more effectively by taking into account the context and arguments at that call. Inlining, however, usually increases the compiled code size, except when only called once or the body is very short, like one line.
Acompiler translates call and return statements into machine instructions according to a well-definedcalling convention. For code compiled by the same or a compatible compiler, functions can be compiled separately from the programs that call them. The instruction sequences corresponding to call and return statements are called the procedure'sprologue and epilogue.
Abuilt-in function, orbuiltin function, orintrinsic function, is a function for which the compiler generates code atcompile time or provides in a way other than for other functions.[23] A built-in function does not need to be defined like other functions since it isbuilt in to the programming language.[24]
Improving readability of code by replacing a block of code with a function call where adescriptive function name serves to describe the block of code. This makes the calling code concise and readable even if the function is not meant to be reused.
Improvingtraceability (i.e. most languages offer ways to obtain the call trace which includes the names of the involved functions and perhaps even more information such as file names and line numbers); by not decomposing the code into functions, debugging would be severely impaired
Many programming conventions have been developed regarding callables.
With respect to naming, many developers name a callable with a phrase starting with averb when it does a certain task, with anadjective when it makes an inquiry, and with anoun when it is used to substitute variables.
Some programmers suggest that a callable should perform exactly one task, and if it performs more than one task, it should be split up into multiple callables. They argue that callables are key components insoftware maintenance, and their roles in the program must remain distinct.
Proponents ofmodular programming advocate that each callable should have minimal dependency on the rest of thecodebase. For example, the use ofglobal variables is generally deemed unwise, because it adds coupling between all callables that use the global variables. If such coupling is not necessary, they advise torefactor callables to accept passedparameters instead.
Early BASIC variants require each line to have a unique number (line number) that orders the lines for execution, provides no separation of the code that is callable, no mechanism for passing arguments or to return a value and all variables are global. It provides the commandGOSUB wheresub is short forsub procedure,subprocedure orsubroutine. Control jumps to the specified line number and then continues on the next line on return.
10REM A BASIC PROGRAM20GOSUB10030GOTO20100INPUT“GIVEMEANUMBER”;N110PRINT“THESQUAREROOTOF”;N;120PRINT“IS”;SQRT(N)130RETURN
This code repeatedly asks the user to enter a number and reports the square root of the value. Lines 100-130 are the callable.
InMicrosoft Small Basic, targeted to the student first learning how to program in a text-based language, a callable unit is called asubroutine. TheSub keyword denotes the start of a subroutine and is followed by a name identifier. Subsequent lines are the body which ends with theEndSub keyword.[25]
In later versions ofVisual Basic (VB), including thelatest product line andVB6, the termprocedure is used for the callable unit concept. The keywordSub is used to return no value andFunction to return a value. When used in the context of a class, a procedure is a method.[27]
Each parameter has adata type that can be specified, but if not, defaults toObject for later versions based on.NET andvariant forVB6.[28]
VB supports parameter passing conventionsby value andby reference via the keywordsByVal andByRef, respectively. UnlessByRef is specified, an argument is passedByVal. Therefore,ByVal is rarely explicitly specified.
For a simple type like a number these conventions are relatively clear. PassingByRef allows the procedure to modify the passed variable whereas passingByVal does not. For an object, semantics can confuse programmers since an object is always treated as a reference. Passing an objectByVal copies the reference; not the state of the object. The called procedure can modify the state of the object via its methods yet cannot modify the object reference of the actual parameter.
SubDoSomething()' Some Code HereEndSub
The does not return a value and has to be called stand-alone, likeDoSomething
This has a side-effect – modifies the variable passed by reference and could be called for variablev likeAddTwo(v). Giving v is 5 before the call, it will be 7 after.
InC andC++, a callable unit is called afunction. A function definition starts with the name of the type of value that it returns orvoid to indicate that it does not return a value. This is followed by the function name, formal arguments in parentheses, and body lines in braces.
InC++, a function declared in aclass (as non-static) is called amember function ormethod. A function outside of a class can be called afree function to distinguish it from a member function.[29]
voiddoSomething(){/* some code */}
This function does not return a value and is always called stand-alone, likedoSomething()
intgiveMeFive(){return5;}
This function returns the integer value 5. The call can be stand-alone or in an expression likey = x + giveMeFive()
voidaddTwo(int*pi){*pi+=2;}
This function has a side-effect – modifies the value passed by address to the input value plus 2. It could be called for variablev asaddTwo(&v) where the ampersand (&) tells the compiler to pass the address of a variable. Giving v is 5 before the call, it will be 7 after.
voidaddTwo(int&i){i+=2;}
This function requires C++ – would not compile as C. It has the same behavior as the preceding example but passes the actual parameter by reference rather than passing its address. A call such asaddTwo(v) does not include an ampersand since the compiler handles passing by reference without syntax in the call.
InPL/I a called procedure may be passed adescriptor providing information about the argument, such as string lengths and array bounds. This allows the procedure to be more general and eliminates the need for the programmer to pass such information. By default PL/I passes arguments by reference. A (trivial) function to change the sign of each element of a two-dimensional array might look like:
This could be called with various arrays as follows:
/* first array bounds from -5 to +10 and 3 to 9 */declare array1 (-5:10, 3:9)float;/* second array bounds from 1 to 16 and 1 to 16 */declare array2 (16,16) float;call change_sign(array1);call change_sign(array2);
InPython, the keyworddef denotes the start of a function definition. The statements of the function body follow as indented on subsequent lines and end at the line that is indented the same as the first line or end of file.[30]
The first function returns greeting text that includes the name passed by the caller. The second function calls the first and is called likegreet_martin() to write "Welcome Martin" to the console.
Notice that the motherhood function,X=mother(Y) is represented by a relation, as in arelational database. However,relations in Prologfunction as callable units.
For example, the procedure call?-parent_child(X,charles) produces the outputX=elizabeth. But the same procedure can be called with other input-output patterns. For example:
^"Terminology Glossary".nist.gov. NIST. Retrieved9 February 2024.Callable unit: (Of a software program or logical design) Function, method, operation, subroutine, procedure, or analogous structural unit that appears within a module.
^Turing, Alan Mathison (19 March 1946) [1945],Proposals for Development in the Mathematics Division of an Automatic Computing Engine (ACE) (NB. Presented on 1946-03-19 before the Executive Committee of the National Physical Laboratory (Great Britain).)
^Frank, Thomas S. (1983).Introduction to the PDP-11 and Its Assembly Language. Prentice-Hall software series. Prentice-Hall. p. 195.ISBN9780134917047. Retrieved6 July 2016.We could supply our assembling clerk with copies of the source code for all of our useful subroutines and then when presenting him with a mainline program for assembly, tell him which subroutines will be called in the mainline [...]
^Verhoeff, Tom (2018)."A Master Class on Recursion". In Böckenhauer, Hans-Joachim; Komm, Dennis; Unger, Walter (eds.).Adventures Between Lower Bounds and Higher Altitudes: Essays Dedicated to Juraj Hromkovič on the Occasion of His 60th Birthday. Springer. p. 616.ISBN978-3-319-98355-4.OCLC1050567095.