Call By Name

A technical answer to the question "What Isa Thunk?" is that function arguments are evaluated in the called function, a la Algol 60 (Algol Sixty).

As an example: When calling a function, if you pass a pointer to hooha() as the first parameter, then every time that first parameter is referenced in the function, hooha() will be called. If hooha() does different things each time it's called (like rand() does), this can cause trouble. It's a feature of some programming languages (anyone care to build a list?).


I believe the original use of thunk comes from Algol Language. Algol 60 had the irritating misfeature of Call By Name arguments; when you called a function Foo(1+2) and it referred to its argument, it was forced to reevaluate 1+2 over and over again.

A thunk is a function pointer packaged up with some data, so you can call 1+2 in the caller's environment. This was so annoying to compiler writers and users both that no other language to my knowledge has followed Algol's questionable lead here; even functional languages like Haskell, which has probably the most non-standard calling conventions of any modern language, evaluate their arguments at most once.

This misfeature was exploited in Jensens Device


'Misfeature' is an interesting word. I think that to refer to call-by-name as a misfeature of Algol is not 100% fair. It seems to me that the problem was not the provision of Call By Name, but rather the failure to provide Call By Reference. This meant that one was forced to use Call By Name in situations where it shouldn't have been necessary. Also there are a few situations where Call By Name simply does not do the job that Call By Reference does. (SWAP(i, a[i]) is the standard example). If Algol had provided 3 options, namely call by value, name, AND reference then I doubt that anyone would have described Call By Name as a misfeature.

In the few situations where it is appropriate, Call By Name is extremely useful. You can achieve much the same effect in languages where it is possible to pass a function, or a pointer to a function, as a parameter, but this is at the expense of having to define a function for every expression you want to pass as a parameter. I have never come across another language where the power of Call By Name is available to the programmer in such a direct and elegant way as in Algol.

-- Michael Davis


If I remember correctly from previous work and education, the point of a Thunk is to delay evaluation of the thing thunked. You can use this idea to emulate a sort of lazy evaluation. Indeed, I think that is how it was first done in Lisp. -- Chris Booth

Like blocks in Smalltalk, then? -- John Clonts

Yes, exactly.


I remember a professor once telling me that thunks were named after the sound made by a chain of activation records on the call stack. Although it could it could have been some other X and Y for X=activation record and Y=call stack. It was a long time ago. -- Michael Feathers


Yes, and I have also read that the word 'thunk' was invented in the early hours of the morning after members of the Algol team had been up for hours struggling with how to implement call-by-name. They had thought that information about the textual form of the parameter would be needed at run-time, causingvarious implementation problems. Then eventually someone realised that it was possible to determine the relevant information at compile time, and put it into a function. Thus the relevant information did not have to be thought out at run-time: there was a function holding the necessary information that the compiler had already thunk out at compile time. -- Michael Davis


This one created the hardest to find bug I have ever had.

I called a routine like so :- myfunc(blah, bloo, rand());

Have you ever notice how your mind has an implicit hardwired expectation that the value of a parameter doesn't mystically change between read only references?

Well, with call-by-name, every time I referred to the third parameter it reevaluated rand() to a different random number.

Bah!

Delayed evaluation would be a Good Thing, but Call By Name was evil.


I beg to differ. This is an example where call-by-name was inappropriate: that doesn't mean it was evil in general. There have been contexts in which I have WANTED a random number generator to produce different results on each evaluation, and have had to implement this in a more cumbersome way than I could have had call-by-name been available. -- Michael Davis


Call By Name was the result of putting the pure Beta Reduction rules of the Lambda Calculus into an eagerly evaluated and impure programming language. Nowadays, we have the call-by-value lambda-calculus, which models (unsurprisingly) Call By Value.


It could be pointed out that Call By Value can be implemented explicitly in a Call By Name language by binding the value of the argument to a local variable, then using that in lieu of the argument. Similarly, Call By Reference can be implemented explicitly by passing a pointer (if the language has them) and binding it locally. Of course, this is something of an ugly hack, and doesn't help when you expect Call By Value semantics by default. Even if you do think to do it, it is an extra step in the code, one which could all to easily be forgotten about in later code maintenance. That, plus the extra overhead of using thunks, makes it an undesirable as a default method of parameter passing. -- Jay Osako


I agree with Michael Davis. When I wrote my own LISP interpreter many years ago (when they were hard to come by outside universities) I incorporated into it a call-by-name possibility along the lines of the Algol thunk. However my default is the usual call-by-binding which is neither call-by-value nor call-by-reference exactly, but an argument must be preceded by the atom NAME to be called by name. There is a distinction between call-by-reference in e.g. C and call-by-name as in Algol60. Call-by-binding has a call-by-reference flavour in that altering a list argument by surgery e.g. using NCONC or REPLACD, changes the value of the external variable. It is like call-by-value in that the pointer of the external variable cannot be changed inside the function. That can be done using call-by-name. I still use this call-by-name facility to good effect e.g. in investigations of recursion in Church's Lambda Calculus. An example of a more practical use is in setting properties of many atoms according to a standard format. -- Nick Thomas


Graham Hughes wrote: "no other language to my knowledge has followed Algol's questionable lead here; even functional languages like Haskell, which has probably the most non-standard calling conventions of any modern language, evaluate their arguments at most once".

I remember that Simula (as Algol's son) allows call by name, but only if a parameter is explicitly declared with the "name" keyword. -- Gian Carlo Macchi


The Cee Preprocessor uses call by name in the extreme. Not only does it re-evaluate arguments at every occurrence, it even reparses statements. For example, an argument "a + b", used in expression "arg * 2" makes it evaluate a + 2b, not 2(a+b).

Reinder

Of course, because you told him so. If you want parentheses, you have to write parentheses. a+b*2 != (a+b)*2


"Algol 60 had the irritating misfeature of Call By Name arguments:" -- Sorry, but I strongly disagree with the former statement. In my concept, call by name is one of the most powerful features of Algol60, timely expected for in newer programming languages. Finally, C#, VB.net and lately announced to be present in Java in the form of Lambda Calculus.

Carlos Lauterbach.


See original on c2.com