1

Although I seriously code with computer languages in general since 2010 and as an amateur programmer with programming languages in particular since 2015 (primarily Bash and JavaScript imperative scripts) and codes I wrote are scattered through my Stack Exchange accounts, as in my Code Review SE account,
I think I still misunderstand what a Callback is;
I feel that the term itself might be misleading, as I understand that callback functions doesn't "call back" anything, but rather "called back" as a reaction to a condition (event).
I understand the alternative term Callafter as significantly controversial between programmers, so I stay with "Callback";
Any Stack Exchange session I tried to read about "what is a Callback?" contained answers that I either recognized as contradicting or controversial by other programmers commenting in comments, or I just didn't recognize something clear from an answer I have read.

My question

Because the particular examples I read of this concept in Bash and JavaScript are not remembered to me as clear, I ask:

What was the first introduction of callback concept of programming?
An example for a first definition of the concept and a defined problem it was aimed to solve, might be helpful.

Update (02/02/20)

From what I did understood from generally all answers, the term "callback" is always context dependent, so it could have essentially different definitions inside both computer science and programming, but I might understand wrong.

7
  • 4
    Looking at the "first introduction" might not help to clarify the concept, because whenever something is used for the first time, the concept is usually not presented in a very clear way. Callbacks are for example used extensively in the X Window System (1984), and the concept is very clear in that framework, but I'd assume the concept itself is a lot older.
    – dirkt
    Jan 26, 2020 at 6:21
  • 2
    I always though the idea was that you supplied an address that some other function later "called back" to. I'm pretty sure it is older than GUI "events".
    – Tomas By
    Jan 26, 2020 at 6:58
  • 1
    It seems to me the specific name of "callback" was created for its use in GUIs. On the other hand, the idea of "passing a function pointer to another routine" is as old as FORTRAN 2 (at least) and was typically used to write a "high level" numerical algorithm where the low level details were delegated to subroutines supplied by its user and passed as arguments. (Note: another-dave posted his answer while I was writing this comment!)
    – alephzero
    Jan 26, 2020 at 13:56
  • lambda calculus (circa 1930) had anonymous functions (callbacks, if you will). The idea originates in mathematics. Consider charting an y=fx(x) graph or calculating a derivative. Jan 26, 2020 at 18:25
  • 3
    @KelvinSherlock Anonymous functions aren't necessarily callbacks and a callback isn't necessarily an anonymous function. In fact, in C, a callback must have a name (unless you are using Apple's blocks extension).
    – JeremyP
    Jan 26, 2020 at 20:57

5 Answers 5

14

Any time you can pass a procedure (function, method) designation (address, reference) as a parameter to a procedure, that is a "callback". The reason why you're passing procedure B to procedure A is so that A can call B at some point, either synchronously or asynchronously.

I think the concept is simply an obvious one which emerges as soon as subroutines (closed subroutines, in the terminology of the time) have been invented.

In many numerical analysis examples (which was one focus of early 'scientific' programming language), the callback is essential. For example, suppose you wish to have a procedure that will determine a definite integral of some (mathematical) function, say by something like Simpson's rule - then the specific function is supplied as a parameter to the generalized integration procedure.

EDSAC

The 'closed subroutine' was invented by David Wheeler for the EDSAC. The seminal book The Preparation Of Programs For An Electronic Digital Computer (Wilkes, Wheeler, and Gill; 1951) describes the technique, which was realized by software convention (the hardware for jump-and-return came later).

Part II of their book, Specification of subroutines, describes routines presumably available at Cambridge. Subroutine G1 solves simultaneous differential equations; it requires an 'auxiliary subroutine' (i.e., provided by the user of G1) to compute values of the first derivative and which is called 4 times for each step in G1.

This seems like a callback, and since it was described simultaneously with the notion of subroutine, by the inventors of the subroutine, it is my proposal to be the first. EDSAC itself became operational in May 1949; the book was published in 1951. The Wheeler Jump (aka subroutine call) was therefore invented somewhere between 1949 and 1951. The subroutine was described in a privately-circulated memo in 1950 so that narrows the timeframe down to 1949--1950.

Early examples - synchronous case

These two aren't presented as contenders for the absolute first, just as examples that it's a long-standing idea.

FORTRAN II for the IBM 7090 supported this sort of callback -- likely it was supported on the 709 as well, but I cannot find a manual for that. The manual I linked to was from 1964 but FORTRAN II dates from 1958.

Algol 60 supported the 'explicit' case of passing procedure (function) parameters in procedure (function) calls, but there was also an implicit case: call-by-name parameters. For call-by-name, the actual argument was evaluated every time it was used in the called procedure, in contrast to call-by-value where the argument is evaluated once before entering the procedure. The general case is implementable by converting the expression as entered for the parameter in the source Algol into a procedure -- termed a 'thunk' at the time it was invented, but which is clearly a 'callback' from the context of the called procedure into the context of the calling procedure.

Asynchronous case

If you think that the essence of a callback is that the callback routine is executed at some future time (after the return from the procedure to which the callback routine was passed), then there are still many early pre-GUI examples.

Any system that supports traps, signals, exceptions, etc., is essentially a callback system. You call a setup function to say "when event E occurs, call procedure P".

The RSX family of DEC minicomputer operating systems used this mechanism for I/O. A system call initiated the I/O operation; the initiate call passed the address of a routine to be called on I/O completion. The system call returned after I/O had been queued to the device. Most async programming would be done in the machine's assembly language. Libraries could and did expose the callback feature to higher level languages such as FORTRAN.

This approach is obviously modelled on what the hardware actually does. You start I/O in progress and later get a completion interrupt. So there's some case for saying the invention of the interrupt was the invention of the callback function. However, as I explained above, my conclusion is that callbacks were invented as an immediate corollary to the invention of subroutines, and thus on EDSAC, prior to the invention of interrupts.

24
  • Re, "I think the concept is simply an obvious one." Even if it really was "simply obvious," somebody still had to invent it. And that includes inventing the hardware mechanism that allows a program branch to or "call" to an address that it fetches from a writeable memory location. Jan 28, 2020 at 14:49
  • 1
    There's no such thing as a computer without some form of jump at the machine level, else it would not be Turing-complete. You don't need hardware support for subroutine calls -- when David Wheeler invented the subroutine, it was at first a software convention. Jan 29, 2020 at 0:03
  • 1
    Having looked at my copy of Wilkes, Wheeler, and Gill, I updated my answer to claim them as the first. Jan 29, 2020 at 0:58
  • @another-dave not complete true. To be Turing-complete it only needs conditional execution, which can be done without jumping - even less the ability to subroutines or jumps to arbitrary addresses. And yes, there have been early machines that were exactly that. Other than that, perfect answer.
    – Raffzahn
    Jan 29, 2020 at 9:17
  • Got a ref to a non-jumping machine? Not disputing, merely interested. Jan 29, 2020 at 12:26
5

"Callbacks" could be considered a special, simplified case of continuation-passing style. The first formal description of this in an implemented language that I'm aware of is in MIT AI Memo 349, "Scheme: An Interpreter for Extended Lambda Calculus," from 1975. Generalized continuations run the risk of making your head explode even if you do understand callbacks very well, but at least the initial example in section 2.3 of the paper is fairly clear, simple example:

Now consider the following alternative definition of FACT. It has an extra argument, the continuation [16], which is a function to call with the answer, when we have it, rather than return a value; that is, rather than ultimately reducing to the desired value, it reduces to a combination which is the application of the continuation to the desired value.

(DEFINE FACT
  (LAMBDA (N C)
    (IF (= N 0) (C 1)
        (FACT (- N 1)
              (LAMBDA (A) (C (* N A)))))))

Here C could also be called a "callback": you pass it to FACT and FACT calls it with the answer it produces. However, this CPS use is slightly different from "typical callback use" in systems like Node.js in that it executes the callback immediately and, especially, does not return to continue executing the code after FACT has been called (though that can be implemented in this style).

(The paper goes on to give examples of where this style is useful and can make code more clear.)

Even at the time this was not a new technique. The reference [16] above is to John C. Reynolds, "Definitional intepreters for higher order proramming languages" in ACM Conference Proceedings, 1972. And I'm sure that other less general versions of this technique had been used for years before this paper.

So the answer here is really, probably, "it's ancient knowledge that's been recycled a lot in various forms."

If it's just a terminology issue that's bugging you, think of "callback" as being short for, "a function that is called (back) later, rather than called immediately."

2
  • 3
    "it's ancient knowledge that's been recycled" Exactly. I know callbacks as part of an OS level API as early as 1965/66 with IBM's BPS/TOS/DOS for the new /360 machines (EXLST in OS/360). But I'm pretty sure they the obvious mechanics were used before that. Naming is much based on how one would ask to be called back IRL.
    – Raffzahn
    Jan 26, 2020 at 10:42
  • 1
    Jerry Sussman, main builder of Scheme had been one of the builders of MDL a few years earlier. MDL, a lisp like language, had a feature called co-routines. This is vaguely related to callbacks. Jan 26, 2020 at 12:25
4

Callback is eventually present since the very first days of computing - right after arbitrary jumps and subroutines were invented :)

The oldest case of organized callback, that is outside some custom package, as part of a standard programming system, I ever encountered is the Exit Routine mechanism under /360 style OS (like DOS/360). Exit Routine is the name used by IBM (at the time) for callbacks.

For example an Exit List (Macro EXLST) could (and should) be provided with a Data Control Block (DCB) used to specify an I/O operation to overwrite default handling for certain situations, like device/block/record not found. This included no only error conditions, but as well handling and progress situations, like tape/disk assignment, operator interaction or begin/end of file.

Similar this mechanic was used for things like timer handling (Routines called when a certain time was passed) or message handling (between processes/tasks).

In addition many high level runtime systems like databases did use a variation thereof. For example, a program could issue custom filter over a record range by providing a callback returning a match/no-match code. The framework would call it with every record and issue the records to the main program when a match happened. This was done to increase thruput due an extreme short code path (no copying, no overhead, just a few machine instructions for selection). Similar field selection call backs could be used and many more. Standard sort utilities worked with similar mechanics.

The basic concept was present since 1965/66, introduced with IBM's BPS/TOS/DOS for the (back then) brand new /360 machines. Then again, I wouldn't be surprised to find standardised use before 1965.


I feel that the term [callback] itself might be misleading, as I understand that callback functions doesn't "call back" anything, but rather "called back" as a reaction to a condition (event)

Well, yes and no. It's all about the viewpoint. When seen fromm the routine, it for sure is "called back", but at the time the routine is installed, it's like saying "call (me) back" whenever the condition is met.

3

It really depends on what you think of as a "callback". If you define it as "arbitrary code provided by an outer routine, to be executed by an inner routine under certain circumstances", I would argue that the concept dates back to the arithmetic overflow handler supported by the original UNIVAC (1951).

Basically, the programmer could put two instructions at a special point in memory, and if an arithmetic operation overflowed, those instructions would be executed before the following one. If the instructions were a transfer of control (a jump), the result would be executing an arbitrary callback in response to the overflow.

The problem is, since UNIVAC had (AFAIK) no way to push or even programmatically read the program counter, control could not automatically return from the handler. For that matter, arithmetic overflow would generally be seen as a nonrecoverable error condition, rather than something to be invoked for routine-specific reasons. So while it technically probably counts as The First Callback, I don't think it satisfies.

Instead, I'd look a little later, to the UNIVAC 1103 (1953). This computer implemented the concept of an "interrupt handler", which could be triggered by the operator or by the program itself. The 1103's interrupt procedure allowed for the program counter to be saved and later returned to, which is the rest of what you'd need for a "call and return" style of callback.

1
  • The two-instruction sequence itself is surely a 'callback'. It must be set up in advance of an overflow event (analogously to establishing any asynchronous callback routine in modern software systems), and it will subsequently get 'called' when the overflow happens. As long as you can service the overflow in 2 words, you can return, right? Set a program overflow flag or set the accumulator to the max value, or whatever. Jan 29, 2020 at 0:12
0
+500

The term callback can have different meaning in different computing discourses (different in computer science and different in programming).

It is further hard to give one general definition because of these reasons:

1. The mechanism of a callback is much older than the term itself

The term starting to become popular since 1985 (see the Google ngram about the popularity of the term over the years), the mechanism is generally described as "any executable code that is passed as an argument to other code that is expected to call back (execute) the argument at a given time." (Wikipedia: Callback (Computer Programming)) This definition is already fulfilled by machine code programs that set an interrupt vector for calling back an interrupt service function when some event occurs.

2. To identify when an interrupt mechanism could be considered a callback

This webpage briefly reviews the history of interrupts starting with the 1953 UNIVAC system which uses an interrupt for exception handling - the process execution jumps to a fixed address when an overflow occurs. However, since the address is fixed this does not fit the definition of callback used as a programming concept. On the other hand processors with a configurable interrupt vector would fulfill this, but somebody might have used a construction like JMP xxxx at the fixed interrupt handler address and then just write the desired function address over the address part of the JMP command.

So while it had not been named this way, it is very likely that people used a callback mechanism in Assembler in the 1950s or 1960s. A patent from 1965 mentions selectable addresses in interrupts, although it is unclear if this could be interpreted as an idea for interrupts to be configured in run-time. Definitely, there is hardware support for configurable interrupt handler addresses in the well-known 8-bit Microcontrollers Motorola 6800 (1974) and MOS 6502 (1975).

16
  • "Popular" is difficult to measure, but I'm pretty sure I was familiar with the term before the early 1980s, using it extensively in a (non-GUI) api I implemented in 1983, without thinking I was inventing anything or even using an obscure term. Feb 1, 2020 at 0:07
  • The Sperry patent is about multiple register sets, by the way. Feb 1, 2020 at 0:13
  • 1
    Hello Peter B. from the OP, I understand from your answer that the term "callback" is always context-dependent and isn't well defined in a "physics" (nor in a computer science way). I further understand that by the first definition, any "common programming" function that gets a parameter (that by principle, will be used with an argument in function call) is a "callback". Please tell me, is that correct?
    – user13751
    Feb 1, 2020 at 3:42
  • @another-dave I know that the patent is aiming about switching multi register sets. But the overall description suggests that being able to set the respective interrupt function might have already been known by that time.
    – Peter B.
    Feb 1, 2020 at 16:09
  • 1
    There's no need to speculate about "a callback mechanism in assembler" as late as the 1960s, since by 1958 we already had the map function in LISP, which clearly takes as an argument another function that it calls.
    – cjs
    Feb 8, 2020 at 2:37

You must log in to answer this question.