Why is function with useless isolated `static` considered impure? - c++

In Wikipedia article on Pure function, there is an example of impure function like this:
void f() {
static int x = 0;
++x;
}
With the remark of "because of mutation of a local static variable".
I wonder why is it impure? It's from unit type to unit type, so it always returns the same result for same input. And it has no side effects, because even despite it has static int variable, it's unobservable by any other function than this f(), so there is no observable mutation of global state that other functions might use.
If one argues that any global mutations are disallowed, regardless of whether they are observable or not, then no real life function can be considered pure ever, because any function would allocate its memory on stack, and allocation is impure, as it involves talking to MMU via OS, and allocated page might be residing in a different physical page, and so on, and so on.
So, why does this useless isolated static int makes function impure?

The result of a pure function is fully defined by its input arguments. Here, the result means not only the returned value, but also the effect in terms of the virtual machine defined by the C/C++ standard. In other words, if the function occasionally exhibits undefined behavior with the same input arguments, it cannot be considered pure (because the behavior is different from one call to another with the same input).
In the particular case with the static local variable, that variable may become the source of a data race if f is called concurrently in multiple threads. Data race means undefined behavior. Another possible source of UB is signed integer overflow, which may eventually happen.

The concept of pure functions seems to only matter in... functional languages? Correct me if I'm wrong. The wikipedia link you provide provides two references near the top, one of which is Professor Frisby's Mostly Adequate Guide to Functional Programming. Where there are several different qualifications for a pure function, including:
does not have any observable side effect
This matters because one of the things we can do to a pure function (as opposed to an impure function) is memoization (from the link above), or input/output caching. Pure functions are also testable, reasonable, and self documenting.
I guess memoization matters for the compiler, so asking if a function is "pure" can be considered equivalent to asking if the compiler can memoize the function. It seems like the concept of a static local variable that no other piece of code touches is just bad code, and the compiler should issue a warning about it. But should the compiler optimize it away? And should the compiler try to figure out if any given static local variable actually has no side effects?
It seems like it's just easier to design the compiler to always flag a function is impure if it has a static local, instead of writing logic to hem and haw over whether the function is memoizable or not. See a local static? Boom: no longer pure.
So from a compiler's point of view, it's impure.
What about the other properties of a pure function?
testable, reasonable, and self documenting
Tests are written by a person, usually, so I'd argue this function is testable. Although some automated test-writing software might again see that it's not memoizable, and just choose to ignore writing tests for it entirely. This hypothetical software might just skip anything with local statics. Again, hypothetically.
Is the code reasonable? Certainly not. Although I'm not sure how much this matters. It doesn't do anything. It makes it hard to understand. ("Why did Bob write the function this way? Is this a Magic/More Magic situation?").
Is the code self-documenting? Again, I'd say not. But again, this is degenerate example code.
I think the biggest argument against this being considered a pure function is that a functional language compiler would be perfectly reasonable if it just assumed it wasn't pure.
I think the biggest argument for this being considered a pure function is that we can look at it with our own eyeballs and see that there's obviously no outside behavior. Ignore the fact that signed overflow is undefined. Replace this with a datatype that has defined overflow, and is atomic. Well now there's no undefined behavior, but it still looks weird.
"In conclusion, I don't care whether it's pure or not."
Let me rephrase from my previous (above) conclusion.
I'm inclined to just scan a function for any mutation of static variables and call it a day. Boom, no longer pure.
Can the function be considered pure if we really think about it? Sure. But what's the point? If the definition of a pure function needs to be changed, argue for it to be changed. Seems like you think this is a pure function. That's fine, I see the merits in that. I also see the merits in considering it an impure function.
As much as this is a non-answer, it really depends on what you're using the definition of pure for. If it's writing a compiler? Probably want to use the more conservative definition of pure that allows false positives and excludes this function. If it's to impress a bunch of sophomore CS students while listening to Zep? Go for the definition that recognizes this has no side effects and call it a day.

Related

forcing a function to be pure

In C++ it is possible to declare that a function is const, which means, as far as I understand, that the compiler ensures the function does not modify the object. Is there something analogous in C++ where I can require that a function is pure? If not in C++, is there a language where one can make this requirement?
If this is not possible, why is it possible to require functions to be const but not require them to be pure? What makes these requirements different?
For clarity, by pure I want there to be no side effects and no use of variables other than those passed into the function. As a result there should be no file reading or system calls etc.
Here is a clearer definition of side effects:
No modification to files on the computer that the program is run on and no modification to variables with scope outside the function. No information is used to compute the function other than variables passed into it. Running the function should return the same thing every time it is run.
NOTE: I did some more research and encountered pure script
(Thanks for jarod42's comment)
Based on a quick read of the wikipedia article I am under the impression you can require functions be pure in pure script, however I am not completely sure.
Short answer: No. There is no equivalent keyword called pure that constrains a function like const does.
However, if you have a specific global variable you'd like to remain untouched, you do have the option of static type myVar. This will require that only functions in that file will be able to use it, and nothing outside of that file. That means any function outside that file will be constrained to leave it alone.
As to "side effects", I will break each of them down so you know what options you have:
No modification to files on the computer that the program is run on.
You can't constrain a function to do this that I'm aware. C++ just doesn't offer a way to constrain a function like this. You can, however, design a function to not modify any files, if you like.
No modification to variables with scope outside the function.
Globals are the only variables you can modify outside a function's scope that I'm aware of, besides anything passed by pointer or reference as a parameter. Globals have the option of being constant or static, which will keep you from modifying them, but, beyond that, there's really nothing you can do that I'm aware.
No information is used to compute the function other than variables passed into it.
Again, you can't constrain it to do so that I'm aware. However, you can design the function to work like this if you want.
Running the function should return the same thing every time it is run.
I'm not sure I understand why you want to constrain a function like this, but no. Not that I'm aware. Again, you can design it like this if you like, though.
As to why C++ doesn't offer an option like this? I'm guessing reusability. It appears that you have a specific list of things you don't want your function to do. However, the likelihood that a lot of other C++ users as a whole will need this particular set of constraints often is very small. Maybe they need one or two at a time, but not all at once. It doesn't seem like it would be worth the trouble to add it.
The same, however, cannot be said about const. const is used all the time, especially in parameter lists. This is to keep data from getting modified if it's passed by reference, or something. Thus, the compiler needs to know what functions modify the object. It uses const in the function declaration to keep track of this. Otherwise, it would have no way of knowing. However, with using const, it's quite simple. It can just constrain the object to only use functions that guarantee that it remains constant, or uses the const keyword in the declaration if the function.
Thus, const get's a lot of reuse.
Currently, C++ does not have a mechanism to ensure that a function has "no side effects and no use of variables other than those passed into the function." You can only force yourself to write pure functions, as mentioned by Jack Bashford. The compiler can't check this for you.
There is a proposal (N3744 Proposing [[pure]]). Here you can see that GCC and Clang already support __attribute__((pure)). Maybe it will be standardized in some form in the future revisions of C++.
In C++ it is possible to declare that a function is const, which means, as far as I understand, that the compiler ensures the function does not modify the object.
Not quite. The compiler will allow the object to be modified by (potentially ill-advised) use of const_cast. So the compiler only ensures that the function does not accidentally modify the object.
What makes these requirements [constant and pure] different?
They are different because one affects correct functionality while the other does not.
Suppose C is a container and you are iterating over its contents. At some point within the loop, perhaps you need to call a function that takes C as a parameter. If that function were to clear() the container, your loop will likely crash. Sure, you could build a loop that can handle that, but the point is that there are times when a caller needs assurance that the rug will not be pulled out from under it. Hence the ability to mark things const. If you pass C as a constant reference to a function, that function is promising to not modify C. This promise provides the needed assurance (even though, as I mentioned above, the promise can be broken).
I am not aware of a case where use of a non-pure function could similarly cause a program to crash. If there is no use for something, why complicate the language with it? If you can come up with a good use-case, maybe it is something to consider for a future revision of the language.
(Knowing that a function is pure could help a compiler optimize code. As far as I know, it's been left up to each compiler to define how to flag that, as it does not affect functionality.)

Can we make virtual function inline [duplicate]

Pure virtual functions are those member functions that are virtual and have the pure-specifier ( = 0; )
Clause 10.4 paragraph 2 of C++03 tells us what an abstract class is and, as a side note, the following:
[Note: a function declaration cannot provide both a pure-specifier and a definition
—end note] [Example:
struct C {
virtual void f() = 0 { }; // ill-formed
};
—end example]
For those who are not very familiar with the issue, please note that pure virtual functions can have definitions but the above-mentioned clause forbids such definitions to appear inline (lexically in-class). (For uses of defining pure virtual functions you may see, for example, this GotW)
Now for all other kinds and types of functions it is allowed to provide an in-class definition, and this restriction seems at first glance absolutely artificial and inexplicable. Come to think of it, it seems such on second and subsequent glances :) But I believe the restriction wouldn't be there if there weren't a specific reason for that.
My question is: does anybody know those specific reasons? Good guesses are also welcome.
Notes:
MSVC does allow PVF's to have inline definitions. So don't get surprised :)
the word inline in this question does not refer to the inline keyword. It is supposed to mean lexically in-class
In the SO thread "Why is a pure virtual function initialized by 0?" Jerry Coffin provided this quote from Bjarne Stroustrup’s The Design & Evolution of C++, section §13.2.3, where I've added some emphasis of the part I think is relevant:
The curious =0 syntax was chosen over the obvious alternative of introducing a new keyword pure or abstract because at the time I saw no chance of getting a new keyword accepted. Had I suggested pure, Release 2.0 would have shipped without abstract classes. Given a choice between a nicer syntax and abstract classes, I chose abstract classes. Rather than risking delay and incurring the certain fights over pure, I used the tradition C and C++ convention of using 0 to represent "not there." The =0 syntax fits with my view that a function body is the initializer for a function and also with the (simplistic, but usually adequate) view of the set of virtual functions being implemented as a vector of function pointers. [ … ]
So, when choosing the syntax Bjarne was thinking of a function body as a kind of initializer part of the declarator, and =0 as an alternate form of initializer, one that indicated “no body” (or in his words, “not there”).
It stands to reason that one cannot both indicate “not there” and have a body – in that conceptual picture.
Or, still in that conceptual picture, having two initializers.
Now, that's as far as my telepathic powers, google-foo and soft-reasoning goes. I surmise that nobody's been Interested Enough™ to formulate a proposal to the committee about having this purely syntactical restriction lifted, and following up with all the work that that entails. Thus it's still that way.
You shouldn't have so much faith in the standardization committee. Not everything has a deep reason to explain it. Something are so just because at first nobody thought otherwise and after nobody thought that changing it is important enough (I think it is the case here); for things old enough it could even be an artifact of the first implementation. Some are the result of evolution -- there was a deep reason at a time, but the reason was removed and the initial decision wasn't reconsidered again (it could be also the case here, where the initial decision was because any definition of the pure function was forbidden). Some are the result of negotiation between different POV and the result lacks coherence but this lack was deemed necessary to reach to consensus.
Good guesses... well, considering the situation:
it is legal to declare the function inline and provide an explicitly inline body (outside the class), so there's clearly no objection to the only practical implication of being declared inside the class.
I see no potential ambiguities or conflicts introduced in the grammar, so no logical reason for the exclusion of function definitions in situ.
My guess: the use for bodies for pure virtual functions was realised after the = 0 | { ... } grammar was formulated, and the grammar simply wasn't revised. It's worth considering that there are a lot of proposals for language changes / enhancements - including those to make things like this more logical and consistent - but the number that are picked up by someone and written up as formal proposals is much smaller, and the number of those the Committee has time to consider, and believes the compiler-vendors will be prepared to implement, is much smaller again. Things like this need a champion, and perhaps you're the first person to see an issue in it. To get a feel for this process, check out http://www2.research.att.com/~bs/evol-issues.html.
Good guesses are welcome you say?
I think the = 0 at the declaration comes from having the implementation in mind. Most likely this definition means, that you get a NULL entry in the RTTI's vtbl of the class information -- the location where at runtime addresses of the member functions of a class are stored.
But actually, when put a definition of the function in your *.cpp file, you introduce a name into the object file for the linker: An address in the *.o file where to find a specific function.
The basic linker then does need to know about C++ anymore. It can just link together, even though you declared it as = 0.
I think I read that it is possible what you described, although I forgot the behaviour :-)...
Leaving destructors aside, implementations of pure virtual functions are a strange thing, because they never get called in the natural way. i.e. if you have a pointer or reference to your Base class the underlying object will always be some Derived that overrides the function, and that will always get called.
The only way to actually get the implementation to be called is using the Base::func() syntax from one of the derived class's overloads.
This actually, in some ways, makes it a better target for inlining, as at the point where the compiler wants to invoke it, it is always clear which overload is being called.
Also, if implementations for pure virtual functions were forbidden, there would be an obvious workaround of some other (probably protected) non-virtual function in the Base class that you could just call in the regular way from your derived function. Of course the scope would be less limited in that you could call it from any function.
(By the way, I am under the assumption that Base::f() can only be called with this syntax from Derived::f() and not from Derived::anyOtherFunc(). Am I right with this assumption?).
Pure virtual destructors are a different story, in a sense. It is used as a technique simply to prevent someone creating an instance of the derived class without there being any pure virtual functions elsewhere.
The answer to the actual question of "why" it is not permitted is really just because the standards committee said so, but my answer sheds some light on what we are trying to achieve anyway.

Times when inline functioning cannot be used

Im studying inline functions in C++ and have come to a section concerning limitations of its use. It says:
The compiler also cannot perform
inlining if the address of the
function is taken implicitly or
explicitly.
Can someone explain to me, perhaps with an example of some sort, what exactly this means?
You can mark any function as inline. Even a virtual function, even a recursive function, even a veeery veery long function, even if its address is taken. The main difference between an inline and non-inline function is that the definition of the former must appear in every translation unit (aka source file) in which it is used ( that's why inline functions are usually defined in the .h file), whereas the latter must be defined only once. You can use the inline function in every way that you can use a non-inline one.
The actual inlining part is up to the compiler. It can ignore your request, if, for example, your function is recursive or too long. On the other hand, the compiler may choose to inline a function which you haven't actually marked as inline.
There are two somewhat separate decisions the compiler makes concerning function inlining:
whether a particular function call is inlined;
whether a non-inline version of the function exists.
The first is decided by the compiler on a case-by-case basis, if inlining is possible at that point. It won't be possible if the function is virtual, or called through a function pointer, and it can't determine at compile time which function is to be called. It won't be possible if the definition is not available to the compiler, perhaps because it is defined in a different translation unit and the compiler does not do "whole program optimisation". The decision may, or may not, be influenced by whether the function is declared inline, and other factors such as its size and how often it is called.
The second depends on whether a non-inline version is required. It will be required if any call to it is not inlined. It will also (as per your quotation) be required if anything needs the address of the function, as then it must have an address. This can happen either directly (for example by assigning the address to a function pointer), or indirectly (for example, virtual functions will need their address stored somewhere to look up at runtime according to the object's dynamic type).
The existence of the non-inline version will not prevent any particular call to the function from being inlined, although it's possible that it might influence the compiler's decision, particularly if it's configured to optimise for code size.
To summarise, your quotation is simplistic and not entirely accurate; the compiler can still "perform inlining" if the address is taken, it just can't omit the non-inline version.
It's just wrong: ability to inline a function call is not affected by computing 2+2 or by taking the address of the function somewhere.
Which book or article are you reading?
On the other hand, if the address is taken then it might be practically impossible to remove the separate machine code function.
Cheers & hth.,

Whyever **not** declare a function to be `constexpr`?

Any function that consists of a return statement only could be declared
constexpr and thus will allow to be evaluated at compile time if all
arguments are constexpr and only constexpr functions are called in its body. Is there any reason not to declare any such function constexpr ?
Example:
constexpr int sum(int x, int y) { return x + y; }
constexpr i = 10;
static_assert(sum(i, 13) == 23, "sum correct");
Could anyone provide an example where declaring a function constexpr
would do any harm?
Some initial thoughts:
Even if there should be no good reason for ever declaring a function
not constexpr I could imagine that the constexpr keyword has a
transitional role: its absence in code that does not need compile-time
evaluations would allow compilers that do not implement compile-time
evaluations still to compile that code (but to fail reliably on code
that needs them as made explict by using constexpr).
But what I do not understand: if there should be no good reason for
ever declaring a function not constexpr, why is not every function
in the standard library declared constexpr? (You cannot argue
that it is not done yet because there was not sufficient time yet to
do it, because doing it for all is a no-brainer -- contrary to deciding for every single function if to make it constexpr or not.)
--- I am aware that N2976
deliberately not requires cstrs for many standard library types such
as the containers as this would be too limitating for possible
implementations. Lets exclude them from the argument and just wonder:
once a type in the standard library actually has a constexpr cstr, why is not every function operating on it declared constexpr?
In most cases you also cannot argue that you may prefer not to declare a function constexpr simply because you do not envisage any compile-time usage: because if others evtl. will use your code, they may see such a use that you do not. (But granted for type trait types and stuff alike, of course.)
So I guess there must be a good reason and a good example for deliberately not declaring a function constexpr?
(with "every function" I always mean: every function that meets the
requirements for being constexpr, i.e., is defined as a single
return statement, takes only arguments of types with constexpr
cstrs and calls only constexpr functions. Since C++14, much more is allowed in the body of such function: e.g., C++14 constexpr functions may use local variables and loops, so an even wider class of functions could be declared constexpr.)
The question Why does std::forward discard constexpr-ness? is a special case of this one.
Functions can only be declared constexpr if they obey the rules for constexpr --- no dynamic casts, no memory allocation, no calls to non-constexpr functions, etc.
Declaring a function in the standard library as constexpr requires that ALL implementations obey those rules.
Firstly, this requires checking for each function that it can be implemented as constexpr, which is a long job.
Secondly, this is a big constraint on the implementations, and will outlaw many debugging implementations. It is therefore only worth it if the benefits outweigh the costs, or the requirements are sufficiently tight that the implementation pretty much has to obey the constexpr rules anyway. Making this evaluation for each function is again a long job.
I think what you're referring to is called partial evaluation. What you're touching on is that some programs can be split into two parts - a piece that requires runtime information, and a piece that can be done without any runtime information - and that in theory you could just fully evaluate the part of the program that doesn't need any runtime information before you even start running the program. There are some programming languages that do this. For example, the D programming language has an interpreter built into the compiler that lets you execute code at compile-time, provided that it meets certain restrictions.
There are a few main challenges in getting partial evaluation working. First, it dramatically complicates the logic of the compiler because the compiler will need to have the ability to simulate all of the operations that you could put into an executable program at compile-time. This, in the worst case, requires you to have a full interpreter inside of the compiler, making a difficult problem (writing a good C++ compiler) and making it orders of magnitude harder to do.
I believe that the reason for the current specification about constexpr is simply to limit the complexity of compilers. The cases it's limited to are fairly simple to check. There's no need to implement loops in the compiler (which could cause a whole other slew of problems, like what happens if you get an infinite loop inside the compiler). It also avoids the compiler potentially having to evaluate statements that could cause segfaults at runtime, such as following a bad pointer.
Another consideration to keep in mind is that some functions have side-effects, such as reading from cin or opening a network connection. Functions like these fundamentally can't be optimized at compile-time, since doing so would require knowledge only available at runtime.
To summarize, there's no theoretical reason you couldn't partially evaluate C++ programs at compile-time. In fact, people do this all the time. Optimizing compilers, for example, are essentially programs that try to do this as much as possible. Template metaprogramming is one instance where C++ programmers try to execute code inside the compiler, and it's possible to do some great things with templates partially because the rules for templates form a functional language, which the compiler has an easier time implementing. Moreover, if you think of the tradeoff between compiler author hours and programming hours, template metaprogramming shows that if you're okay making programmers bend over backwards to get what they want, you can build a pretty weak language (the template system) and keep the language complexity simple. (I say "weak" as in "not particularly expressive," not "weak" in the computability theory sense).
Hope this helps!
If the function has side effects, you would not want to mark it constexpr. Example
I can't get any unexpected results from that, actually it looks like gcc 4.5.1 just ignores constexpr

What is a 'thunk'?

I've seen it used in programming (specifically in the C++ domain) and have no idea what it is. Presumably it is a design pattern, but I could be wrong. Can anyone give a good example of a thunk?
A thunk usually refers to a small piece of code that is called as a function, does some small thing, and then JUMPs to another location (usually a function) instead of returning to its caller. Assuming the JUMP target is a normal function, when it returns, it will return to the thunk's caller.
Thunks can be used to implement lots of useful things efficiently
protocol translation -- when calling from code that uses one calling convention to code that uses a different calling convention, a thunk can be used to translate the arguments appropriately. This only works if the return conventions are compatible, but that is often the case
virtual function handling -- when calling a virtual function of a multiply-inherited base class in C++, there needs to be a fix-up of the this pointer to get it to point to the right place. A thunk can do this.
dynamic closures -- when you build a dynamic closure, the closure function needs to be able to get at the context where it was created. A small thunk can be built (usually on the stack) which sets up the context info in some register(s) and then jumps to a static piece of code that implements the closure's function. The thunk here is effectively supplying one or more hidden extra arguments to the function that are not provided by the call site.
The word thunk has at least three related meanings in computer science. A "thunk" may be:
a piece of code to perform a delayed
computation (similar to a closure)
a feature of some virtual function
table implementations (similar to a
wrapper function)
a mapping of machine data from one
system-specific form to another,
usually for compatibility reasons
I have usually seen it used in the third context.
http://en.wikipedia.org/wiki/Thunk
The term thunk originally referred to the mechanism used by the Royal Radar Establishment implementation of pass-by-name in their Algol60 compiler. In general it refers to any way to induce dynamic behavior when referencing an apparently static object. The term was invented by Brian Wichmann, who when asked to explain pass-by-name said "Well you go out to load the value from memory and then suddenly - thunk - there you are evaluating an expression."
Thunks have been put in hardware (cf. KDF9, Burroughs mainframes). There are several ways to implement them in software, all very machine, language and compiler specific.
The term has come to be generalized beyond pass-by-name, to include any situation in which an apparently or nominally static data reference induces dynamic behavior. Related terms include "trampoline" and "future".
Some compilers for object-oriented languages such as C++ generate functions called "thunks" as an optimization of virtual function calls in the presence of multiple or virtual inheritance.
Taken from: http://en.wikipedia.org/wiki/Thunk#Thunks_in_object-oriented_programming
This question has already been asked on SO, see:
What is a 'thunk', as used in Scheme or in general?
From what I can tell, it's akin to a lambda statement, where you may not want to return the value until you need to evaluate it; or it can also be compared to a property getter which by design executes some code in order to return a value while yet having the interface form that comes across more like a variable, but also has polymorphic behavior that can be swapped out whether by inheritance or by swapping out the function pointer that would evaluate and return a value at runtime based on compile-time or environmental characteristics.
There's considerable variation in use. Almost universally, a thunk is a function that's (at least conceptually) unusually small and simple. It's usually some sort of adapter that gives you the correct interface to something or other (some data, another function, etc.) but is at least seen as doing little else.
It's almost like a form of syntactic sugar, except that (at least as usually used) syntactic sugar is supposed to make things look the way the human reader wants to see them, and a thunk is to make something look the way the compiler wants to see it.
I was distressed to find no general 'computer science' definition of this term matching its de-facto usage as known historically to me. The first real-life encounter I can recall where it was actually called that was in the OS/2 days and the 16-32 bit transition. It appears "thunking" is like irony in its application today.
My rough general understanding is that the thunk is a stub routine that just does nothing or routes across some fundamental boundary in kind between systems as in the mentioned historical cases.
So the sense is like a synesthesia of being dropped from the one environment to the other making (metaphorically/as a simile) a "thunk" sound.
I'm going to look this up, but I thought thunking was the process employed by a 32-bit processor to run legacy 16-bit code.
I used to use it as an analogy for how you have to restrict how fast you talk and what words you use when talking to dumb people.
Yeah, it's in the Wikipedia link (the part about 32-bit, not my nerdalogy).
https://en.wikipedia.org/wiki/Thunk
Much of the literature on interoperability thunks relates to various Wintel platforms, including MS-DOS, OS/2,[8]Windows[9][10] and .NET, and to the transition from 16-bit to 32-bit memory addressing. As customers have migrated from one platform to another, thunks have been essential to support legacy software written for the older platforms.
(emphasis added by me)
The earliest use of "thunk" I know of is from late '50s in reference to Algol60 pass-by-name argument evaluation in function calls. Algol was originally a specification language, not a programming language, and there was some question about how pass-by-name could be implemented on a computer.
The solution was to pass the entry point of what was essentially a lambda. When the callee evaluated the parameter, control fell through - thunk! - into the caller's context where the lambda was evaluated and it's result became the value of the parameter in the callee.
In tagged hardware, such as the Burroughs machines, the evaluation was implicit: an argument could be passed as a data value as in ordinary pass-by-value, or by thunk for pass-by-name, with different tags in the argument metadata. A load operation hardware checked the tag and either returned the simple value or automatically invoked the lambda thunk.
Per Kyle Simpson's definition, a thunk is a way to abstract the component of time out of asynchronous code.
An earlier version of The New Hacker's Dictionary claimed a thunk is a function that takes no arguments, and that it was a simple late-night solution to a particularly tricky problem, with "thunk" being the supposed past tense of "think", because they ought to have thunk of it long time ago.
In OCaml, it’s a function which takes unit “()” as a param (takes no params, often used for side effects)