A technical answer to the question "WhatIsaThunk
?" is that function arguments are evaluated in the called function, a la Algol 60 (AlgolSixty
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 AlgolLanguage
. Algol 60 had the irritating misfeature of CallByName
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 JensensDevice
'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 CallByName
, but rather the failure to provide CallByReference
. This meant that one was forced to use CallByName
in situations where it shouldn't have been necessary. Also there are a few situations where CallByName
simply does not do the job that CallByReference
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 CallByName
as a misfeature.
In the few situations where it is appropriate, CallByName
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 CallByName
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. -- ChrisBooth
Like blocks in Smalltalk, then? -- JohnClonts
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. -- MichaelFeathers
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.
Delayed evaluation would be a Good Thing, but CallByName
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
was the result of putting the pure BetaReduction?
into an eagerly evaluated and impure programming language.
Nowadays, we have the call-by-value lambda-calculus, which models (unsurprisingly) CallByValue
It could be pointed out that CallByValue
can be implemented explicitly in a CallByName
language by binding the value of the argument to a local variable, then using that in lieu of the argument. Similarly, CallByReference
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 CallByValue
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. -- JayOsako
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
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
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).
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 CallByName
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.